20260314:1、网关设备支持接收子设备上报数据调试;
2、网关设备新增轮询管理页面(支持统一管理子设备轮询任务); 3、历史记录-网关设备新增子设备下拉框; 4、网关子设备不再展示到设备管理页面,入口设备管理-网关设备子设备-详情
This commit is contained in:
parent
9536ae19b1
commit
afdadf84b9
@ -77,4 +77,14 @@ public class DeviceReportBo {
|
|||||||
|
|
||||||
private String sources;
|
private String sources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关子设备BO
|
||||||
|
*/
|
||||||
|
private GwDeviceBo gwDeviceBo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备名称
|
||||||
|
*/
|
||||||
|
private String deviceName;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,8 +109,13 @@ public class DataHandlerImpl implements IDataHandler {
|
|||||||
pushObj.put("sources", bo.getSources());
|
pushObj.put("sources", bo.getSources());
|
||||||
messageBo.setMessage(JSON.toJSONString(pushObj));
|
messageBo.setMessage(JSON.toJSONString(pushObj));
|
||||||
remoteManager.pushCommon(messageBo);
|
remoteManager.pushCommon(messageBo);
|
||||||
if (!Objects.isNull(bo.getGwDeviceBo())) {
|
if (!Objects.isNull(bo.getGwDeviceBo())
|
||||||
|
&& !bo.getSerialNumber().equalsIgnoreCase(bo.getGwDeviceBo().getSerialNumber())) {
|
||||||
|
pushObj.put("subSerialNumber", bo.getSerialNumber());
|
||||||
|
pushObj.put("subDeviceName", bo.getDeviceName());
|
||||||
|
pushObj.put("subSlaveId", bo.getSlaveId());
|
||||||
messageBo.setTopic(topicsUtils.buildTopic(bo.getGwDeviceBo().getProductId(), bo.getGwDeviceBo().getSerialNumber(), TopicType.WS_SERVICE_INVOKE));
|
messageBo.setTopic(topicsUtils.buildTopic(bo.getGwDeviceBo().getProductId(), bo.getGwDeviceBo().getSerialNumber(), TopicType.WS_SERVICE_INVOKE));
|
||||||
|
messageBo.setMessage(JSON.toJSONString(pushObj));
|
||||||
remoteManager.pushCommon(messageBo);
|
remoteManager.pushCommon(messageBo);
|
||||||
}
|
}
|
||||||
// 上报属性给小度音箱,接入小度音箱后可放开
|
// 上报属性给小度音箱,接入小度音箱后可放开
|
||||||
|
|||||||
@ -16,14 +16,12 @@ import com.fastbee.common.core.redis.RedisKeyBuilder;
|
|||||||
import com.fastbee.common.enums.*;
|
import com.fastbee.common.enums.*;
|
||||||
import com.fastbee.common.exception.ServiceException;
|
import com.fastbee.common.exception.ServiceException;
|
||||||
import com.fastbee.common.utils.DateUtils;
|
import com.fastbee.common.utils.DateUtils;
|
||||||
import com.fastbee.common.utils.StringUtils;
|
|
||||||
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
|
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
|
||||||
import com.fastbee.iot.cache.IOtaTaskCache;
|
import com.fastbee.iot.cache.IOtaTaskCache;
|
||||||
import com.fastbee.iot.data.executor.PowerOnTriggerTaskExecutor;
|
import com.fastbee.iot.data.executor.PowerOnTriggerTaskExecutor;
|
||||||
import com.fastbee.iot.data.service.IDataHandler;
|
import com.fastbee.iot.data.service.IDataHandler;
|
||||||
import com.fastbee.iot.data.service.IDeviceReportMessageService;
|
import com.fastbee.iot.data.service.IDeviceReportMessageService;
|
||||||
import com.fastbee.iot.data.service.IFirmwareTaskDetailService;
|
import com.fastbee.iot.data.service.IFirmwareTaskDetailService;
|
||||||
import com.fastbee.iot.domain.Device;
|
|
||||||
import com.fastbee.iot.domain.FunctionLog;
|
import com.fastbee.iot.domain.FunctionLog;
|
||||||
import com.fastbee.iot.domain.Product;
|
import com.fastbee.iot.domain.Product;
|
||||||
import com.fastbee.iot.domain.ProductAuthorize;
|
import com.fastbee.iot.domain.ProductAuthorize;
|
||||||
@ -43,8 +41,6 @@ import io.netty.buffer.Unpooled;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.scheduling.annotation.Async;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@ -297,6 +293,7 @@ public class DeviceReportMessageServiceImpl implements IDeviceReportMessageServi
|
|||||||
report.setUserId(message.getUserId());
|
report.setUserId(message.getUserId());
|
||||||
report.setUserName(message.getUserName());
|
report.setUserName(message.getUserName());
|
||||||
report.setDeviceName(message.getDeviceName());
|
report.setDeviceName(message.getDeviceName());
|
||||||
|
report.setSlaveId(message.getSlaveId());
|
||||||
report.setSources(message.getSources());
|
report.setSources(message.getSources());
|
||||||
report.setGwDeviceBo(message.getGwDeviceBo());
|
report.setGwDeviceBo(message.getGwDeviceBo());
|
||||||
// 属性上报执行规则引擎
|
// 属性上报执行规则引擎
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.alibaba.fastjson2.JSONObject;
|
|||||||
import com.fastbee.common.annotation.SysProtocol;
|
import com.fastbee.common.annotation.SysProtocol;
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.mq.DeviceReport;
|
import com.fastbee.common.core.mq.DeviceReport;
|
||||||
|
import com.fastbee.common.core.mq.GwDeviceBo;
|
||||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||||
import com.fastbee.common.core.mq.SubDeviceBo;
|
import com.fastbee.common.core.mq.SubDeviceBo;
|
||||||
import com.fastbee.common.core.mq.message.DeviceData;
|
import com.fastbee.common.core.mq.message.DeviceData;
|
||||||
@ -111,6 +112,10 @@ public class ModbusToJsonHPProtocolService implements IProtocol {
|
|||||||
result.add(simpleItem);
|
result.add(simpleItem);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
GwDeviceBo gwDeviceBo = new GwDeviceBo();
|
||||||
|
gwDeviceBo.setProductId(deviceData.getProductId());
|
||||||
|
gwDeviceBo.setSerialNumber(deviceData.getSerialNumber());
|
||||||
|
reportMessage.setGwDeviceBo(gwDeviceBo);
|
||||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||||
String slaveKey = entry.getKey();
|
String slaveKey = entry.getKey();
|
||||||
Integer slaveId = StringUtils.matcherNum(slaveKey);
|
Integer slaveId = StringUtils.matcherNum(slaveKey);
|
||||||
@ -146,6 +151,8 @@ public class ModbusToJsonHPProtocolService implements IProtocol {
|
|||||||
report.setSerialNumber(vo.getSubDeviceNo());
|
report.setSerialNumber(vo.getSubDeviceNo());
|
||||||
report.setProductId(vo.getSubProductId());
|
report.setProductId(vo.getSubProductId());
|
||||||
report.setClientId(vo.getSubDeviceNo());
|
report.setClientId(vo.getSubDeviceNo());
|
||||||
|
report.setDeviceName(vo.getSubDeviceName());
|
||||||
|
report.setSlaveId(vo.getSlaveId());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -391,6 +391,8 @@ public class ModbusProtocol implements IProtocol {
|
|||||||
report.setSubDeviceBo(vo);
|
report.setSubDeviceBo(vo);
|
||||||
report.setSerialNumber(vo.getSubDeviceNo());
|
report.setSerialNumber(vo.getSubDeviceNo());
|
||||||
report.setProductId(vo.getSubProductId());
|
report.setProductId(vo.getSubProductId());
|
||||||
|
report.setDeviceName(vo.getSubDeviceName());
|
||||||
|
report.setSlaveId(vo.getSlaveId());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject;
|
|||||||
import com.fastbee.common.annotation.SysProtocol;
|
import com.fastbee.common.annotation.SysProtocol;
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.mq.DeviceReport;
|
import com.fastbee.common.core.mq.DeviceReport;
|
||||||
|
import com.fastbee.common.core.mq.GwDeviceBo;
|
||||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||||
import com.fastbee.common.core.mq.SubDeviceBo;
|
import com.fastbee.common.core.mq.SubDeviceBo;
|
||||||
import com.fastbee.common.core.mq.message.DeviceData;
|
import com.fastbee.common.core.mq.message.DeviceData;
|
||||||
@ -76,6 +77,10 @@ public class ModbusTcpProtocol implements IProtocol {
|
|||||||
// report.setSources(hexDump);
|
// report.setSources(hexDump);
|
||||||
// return report;
|
// return report;
|
||||||
// }
|
// }
|
||||||
|
GwDeviceBo gwDeviceBo = new GwDeviceBo();
|
||||||
|
gwDeviceBo.setProductId(deviceData.getProductId());
|
||||||
|
gwDeviceBo.setSerialNumber(deviceData.getSerialNumber());
|
||||||
|
report.setGwDeviceBo(gwDeviceBo);
|
||||||
matchSubDev(report, deviceData.getSerialNumber(), slaveId);
|
matchSubDev(report, deviceData.getSerialNumber(), slaveId);
|
||||||
String serialNumber = report.getSerialNumber();
|
String serialNumber = report.getSerialNumber();
|
||||||
Long productId = report.getProductId();
|
Long productId = report.getProductId();
|
||||||
@ -366,6 +371,8 @@ public class ModbusTcpProtocol implements IProtocol {
|
|||||||
report.setSubDeviceBo(vo);
|
report.setSubDeviceBo(vo);
|
||||||
report.setSerialNumber(vo.getSubDeviceNo());
|
report.setSerialNumber(vo.getSubDeviceNo());
|
||||||
report.setProductId(vo.getSubProductId());
|
report.setProductId(vo.getSubProductId());
|
||||||
|
report.setDeviceName(vo.getSubDeviceName());
|
||||||
|
report.setSlaveId(vo.getSlaveId());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -142,12 +142,16 @@ public class DispatcherHandler extends ChannelInboundHandlerAdapter {
|
|||||||
reportBo.setThingsModelSimpleItem(report.getThingsModelSimpleItem());
|
reportBo.setThingsModelSimpleItem(report.getThingsModelSimpleItem());
|
||||||
reportBo.setPlatformDate(DateUtils.getNowDate());
|
reportBo.setPlatformDate(DateUtils.getNowDate());
|
||||||
reportBo.setSerialNumber(report.getClientId());
|
reportBo.setSerialNumber(report.getClientId());
|
||||||
|
reportBo.setProductId(report.getProductId());
|
||||||
reportBo.setServerType(ServerType.TCP);
|
reportBo.setServerType(ServerType.TCP);
|
||||||
reportBo.setReplyMessage(report.getReplyMessage());
|
reportBo.setReplyMessage(report.getReplyMessage());
|
||||||
reportBo.setIsReply(report.getIsReply());
|
reportBo.setIsReply(report.getIsReply());
|
||||||
reportBo.setStatus(report.getStatus());
|
reportBo.setStatus(report.getStatus());
|
||||||
reportBo.setProtocolCode(FastBeeConstant.PROTOCOL.ModbusRtu);
|
reportBo.setProtocolCode(FastBeeConstant.PROTOCOL.ModbusRtu);
|
||||||
reportBo.setSources(report.getSources());
|
reportBo.setSources(report.getSources());
|
||||||
|
reportBo.setSlaveId(report.getSlaveId());
|
||||||
|
reportBo.setGwDeviceBo(report.getGwDeviceBo());
|
||||||
|
reportBo.setDeviceName(report.getDeviceName());
|
||||||
MessageProducer.sendPublishMsg(reportBo);
|
MessageProducer.sendPublishMsg(reportBo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,16 +2,14 @@ package com.fastbee.iot.domain;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
|
||||||
import com.fastbee.common.annotation.Excel;
|
import com.fastbee.common.annotation.Excel;
|
||||||
import com.fastbee.common.core.domain.BaseEntity;
|
import com.fastbee.common.core.domain.BaseEntity;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
|
||||||
import org.springframework.data.annotation.Transient;
|
import org.springframework.data.annotation.Transient;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@ -239,6 +237,11 @@ public class Device extends BaseEntity {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private Boolean showChild;
|
private Boolean showChild;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
@ApiModelProperty("查询入口(1=设备管理列表 2=数据中心 3=全部设备)")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer entrance;
|
||||||
|
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private List<Device> subDeviceList;
|
private List<Device> subDeviceList;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.fastbee.iot.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备查询入口枚举,由前端传入,控制不同页面的设备过滤策略
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum DeviceEntrance {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备管理列表 —— 过滤子设备(device_type=4),只展示直连/网关/监控设备
|
||||||
|
*/
|
||||||
|
DEVICE_MANAGE(1, "设备管理列表"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据中心(历史数据) —— 主设备下拉,子设备通过网关接口单独加载
|
||||||
|
*/
|
||||||
|
DATA_CENTER(2, "数据中心"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备全量查询 —— 不做任何过滤,返回所有设备
|
||||||
|
*/
|
||||||
|
DEVICE_ALL(3, "全部设备");
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public static DeviceEntrance fromCode(Integer code) {
|
||||||
|
if (code == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (DeviceEntrance value : DeviceEntrance.values()) {
|
||||||
|
if (value.code == code) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1125,66 +1125,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (DeviceType.GATEWAY.getCode() == device.getDeviceType()) {
|
} else if (DeviceType.GATEWAY.getCode() == device.getDeviceType()) {
|
||||||
// LambdaQueryWrapper<ProductSubGateway> queryWrapper = new LambdaQueryWrapper<>();
|
if (FastBeeConstant.PROTOCOL.ModbusTcp.equals(product.getProtocolCode())) {
|
||||||
// queryWrapper.eq(ProductSubGateway::getGwProductId, product.getProductId());
|
redisCache.getCacheModbusTcpId(device.getSerialNumber());
|
||||||
// 处理子设备添加和子设备轮询
|
|
||||||
ProductSubGateway productSubGateway = new ProductSubGateway();
|
|
||||||
productSubGateway.setGwProductId(product.getProductId());
|
|
||||||
List<ProductSubGatewayVO> productSubGatewayList = productSubGatewayMapper.selectListVO(productSubGateway);
|
|
||||||
if (CollectionUtils.isEmpty(productSubGatewayList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
productSubGatewayList.sort(Comparator.comparing(ProductSubGatewayVO::getSlaveId));
|
|
||||||
List<Long> subProductIdList = productSubGatewayList.stream().map(ProductSubGatewayVO::getSubProductId)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
LambdaQueryWrapper<ProductModbusJob> queryWrapper = new LambdaQueryWrapper<>();
|
|
||||||
queryWrapper.in(ProductModbusJob::getProductId, subProductIdList);
|
|
||||||
List<ProductModbusJob> productModbusJobList = productModbusJobMapper.selectList(queryWrapper);
|
|
||||||
Map<Long, List<ProductModbusJob>> productModbusJobMap = productModbusJobList.stream()
|
|
||||||
.collect(Collectors.groupingBy(ProductModbusJob::getProductId));
|
|
||||||
// 新增子设备、绑定子设备、子设备生成轮询
|
|
||||||
for (ProductSubGatewayVO productSubGatewayVO : productSubGatewayList) {
|
|
||||||
Device addDevice = new Device();
|
|
||||||
addDevice.setDeviceName(
|
|
||||||
productSubGatewayVO.getSubProductName() + "_" + productSubGatewayVO.getSlaveId());
|
|
||||||
addDevice.setProductId(productSubGatewayVO.getSubProductId());
|
|
||||||
addDevice.setProductName(productSubGatewayVO.getSubProductName());
|
|
||||||
String serialNumber = this.generationDeviceNum(1);
|
|
||||||
addDevice.setSerialNumber(serialNumber);
|
|
||||||
addDevice.setTenantId(device.getTenantId());
|
|
||||||
addDevice.setTenantName(device.getTenantName());
|
|
||||||
addDevice.setCreateBy(device.getCreateBy());
|
|
||||||
addDevice.setFirmwareVersion(new BigDecimal(1));
|
|
||||||
addDevice.setRssi(0);
|
|
||||||
addDevice.setIsShadow(0);
|
|
||||||
addDevice.setLocationWay(productSubGatewayVO.getSubLocationWay());
|
|
||||||
addDevice.setImgUrl(product.getImgUrl());
|
|
||||||
addDevice.setCreateTime(new Date());
|
|
||||||
deviceMapper.insertDevice(addDevice);
|
|
||||||
SubGateway subGateway = new SubGateway();
|
|
||||||
subGateway.setGwDeviceId(device.getDeviceId());
|
|
||||||
subGateway.setSubDeviceId(addDevice.getDeviceId());
|
|
||||||
subGateway.setSlaveId(productSubGatewayVO.getSlaveId());
|
|
||||||
subGateway.setCreateBy(addDevice.getCreateBy());
|
|
||||||
subGateway.setCreateTime(new Date());
|
|
||||||
subGatewayMapper.insertGateway(subGateway);
|
|
||||||
List<ProductModbusJob> productModbusList = productModbusJobMap
|
|
||||||
.get(productSubGatewayVO.getSubProductId());
|
|
||||||
if (!CollectionUtils.isEmpty(productModbusList)) {
|
|
||||||
for (ProductModbusJob productModbusJob : productModbusList) {
|
|
||||||
ModbusJob modbusJob = new ModbusJob();
|
|
||||||
modbusJob.setJobName(productModbusJob.getJobName());
|
|
||||||
modbusJob.setSubDeviceId(addDevice.getDeviceId());
|
|
||||||
modbusJob.setSubSerialNumber(addDevice.getSerialNumber());
|
|
||||||
modbusJob.setCommand(productModbusJob.getCommand());
|
|
||||||
modbusJob.setStatus(productModbusJob.getStatus());
|
|
||||||
modbusJob.setRemark(productModbusJob.getRemark());
|
|
||||||
modbusJobService.insertModbusJob(modbusJob);
|
|
||||||
if (FastBeeConstant.PROTOCOL.ModbusTcp.equals(product.getProtocolCode())) {
|
|
||||||
redisCache.getCacheModbusTcpId(device.getSerialNumber());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -398,6 +398,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="groupId != null and groupId !=0 "> left join iot_device_group g on g.device_id=d.device_id </if>
|
<if test="groupId != null and groupId !=0 "> left join iot_device_group g on g.device_id=d.device_id </if>
|
||||||
<where>
|
<where>
|
||||||
and d.gw_dev_code is null
|
and d.gw_dev_code is null
|
||||||
|
<if test="entrance != null and entrance == 1">
|
||||||
|
and (p.device_type is null or p.device_type != 4)
|
||||||
|
</if>
|
||||||
<if test="groupId != null and groupId !=0 "> and g.group_id = #{groupId}</if>
|
<if test="groupId != null and groupId !=0 "> and g.group_id = #{groupId}</if>
|
||||||
<if test="deviceName != null and deviceName != ''"> and d.device_name like concat('%', #{deviceName}, '%')</if>
|
<if test="deviceName != null and deviceName != ''"> and d.device_name like concat('%', #{deviceName}, '%')</if>
|
||||||
<if test="productId != null "> and d.product_id = #{productId}</if>
|
<if test="productId != null "> and d.product_id = #{productId}</if>
|
||||||
@ -881,6 +884,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="groupId != null and groupId !=0 "> left join iot_device_group g on g.device_id=d.device_id </if>
|
<if test="groupId != null and groupId !=0 "> left join iot_device_group g on g.device_id=d.device_id </if>
|
||||||
<where>
|
<where>
|
||||||
and d.gw_dev_code is null
|
and d.gw_dev_code is null
|
||||||
|
<if test="entrance != null and entrance == 1">
|
||||||
|
and (p.device_type is null or p.device_type != 4)
|
||||||
|
</if>
|
||||||
<if test="groupId != null and groupId !=0 "> and g.group_id = #{groupId}</if>
|
<if test="groupId != null and groupId !=0 "> and g.group_id = #{groupId}</if>
|
||||||
<if test="deviceName != null and deviceName != ''"> and d.device_name like concat('%', #{deviceName}, '%')</if>
|
<if test="deviceName != null and deviceName != ''"> and d.device_name like concat('%', #{deviceName}, '%')</if>
|
||||||
<if test="productId != null "> and d.product_id = #{productId}</if>
|
<if test="productId != null "> and d.product_id = #{productId}</if>
|
||||||
|
|||||||
@ -104,6 +104,7 @@
|
|||||||
"device.device-edit.148398-75": "video surveillance",
|
"device.device-edit.148398-75": "video surveillance",
|
||||||
"device.device-edit.148398-76": "Data debugging",
|
"device.device-edit.148398-76": "Data debugging",
|
||||||
"device.device-edit.148398-77": "Polling task",
|
"device.device-edit.148398-77": "Polling task",
|
||||||
|
"device.device-edit.subDevicePollingManage": "Polling management",
|
||||||
"device.device-edit.148398-78": "Gateway",
|
"device.device-edit.148398-78": "Gateway",
|
||||||
"device.device-edit.148398-79": "Cloud recording",
|
"device.device-edit.148398-79": "Cloud recording",
|
||||||
"device.device-edit.148398-80": "Alert user",
|
"device.device-edit.148398-80": "Alert user",
|
||||||
|
|||||||
@ -104,6 +104,7 @@
|
|||||||
"device.device-edit.148398-75": "视频监控",
|
"device.device-edit.148398-75": "视频监控",
|
||||||
"device.device-edit.148398-76": "数据调试",
|
"device.device-edit.148398-76": "数据调试",
|
||||||
"device.device-edit.148398-77": "轮询任务",
|
"device.device-edit.148398-77": "轮询任务",
|
||||||
|
"device.device-edit.subDevicePollingManage": "轮询管理",
|
||||||
"device.device-edit.148398-78": "网关",
|
"device.device-edit.148398-78": "网关",
|
||||||
"device.device-edit.148398-79": "云端录像",
|
"device.device-edit.148398-79": "云端录像",
|
||||||
"device.device-edit.148398-80": "告警用户",
|
"device.device-edit.148398-80": "告警用户",
|
||||||
|
|||||||
@ -10,6 +10,11 @@
|
|||||||
<el-option v-for="(item, index) in devDeviceList" :key="index" :label="item.deviceName" :value="item.deviceId"></el-option>
|
<el-option v-for="(item, index) in devDeviceList" :key="index" :label="item.deviceName" :value="item.deviceId"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item v-if="devSubDeviceList.length > 0" label="子设备" prop="subDeviceId">
|
||||||
|
<el-select style="width: 100%" v-model="devQueryParams.subDeviceId" placeholder="请选择子设备" filterable @change="handleDevSubDeviceChange">
|
||||||
|
<el-option v-for="item in devSubDeviceList" :key="item.subDeviceId" :label="item.subDeviceName + ' (从机' + item.slaveId + ')'" :value="item.subDeviceId"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item :label="$t('dataCenter.history.384934-3')" prop="identifiers">
|
<el-form-item :label="$t('dataCenter.history.384934-3')" prop="identifiers">
|
||||||
<el-select style="width: 100%" v-model="devQueryParams.identifiers" :placeholder="$t('dataCenter.history.384934-4')" filterable multiple collapse-tags @change="handleDevIdentifierChange">
|
<el-select style="width: 100%" v-model="devQueryParams.identifiers" :placeholder="$t('dataCenter.history.384934-4')" filterable multiple collapse-tags @change="handleDevIdentifierChange">
|
||||||
<el-option label="全部勾选/反选" value="__selectAll__">
|
<el-option label="全部勾选/反选" value="__selectAll__">
|
||||||
@ -148,6 +153,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { listDeviceShort, listThingsModel } from '@/api/iot/device.js';
|
import { listDeviceShort, listThingsModel } from '@/api/iot/device.js';
|
||||||
|
import { listGateway } from '@/api/iot/gateway.js';
|
||||||
import { getSceneModelList, getSceneModelDetail, getSceneModelDataList } from '@/api/scene/list.js';
|
import { getSceneModelList, getSceneModelDetail, getSceneModelDataList } from '@/api/scene/list.js';
|
||||||
import { getDataCenterDeviceHistory, getDataCenterSceneHistory, exportDeviceHistory } from '@/api/data/center.js';
|
import { getDataCenterDeviceHistory, getDataCenterSceneHistory, exportDeviceHistory } from '@/api/data/center.js';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
@ -159,6 +165,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
activeTab: 'device',
|
activeTab: 'device',
|
||||||
devDeviceList: [], // 设备-设备列表
|
devDeviceList: [], // 设备-设备列表
|
||||||
|
devSubDeviceList: [], // 网关子设备列表
|
||||||
devIdentifierList: [],
|
devIdentifierList: [],
|
||||||
pickerOptions: {
|
pickerOptions: {
|
||||||
shortcuts: [
|
shortcuts: [
|
||||||
@ -212,6 +219,7 @@ export default {
|
|||||||
// 查询参数
|
// 查询参数
|
||||||
devQueryParams: {
|
devQueryParams: {
|
||||||
deviceId: null,
|
deviceId: null,
|
||||||
|
subDeviceId: null,
|
||||||
identifiers: [],
|
identifiers: [],
|
||||||
dayDaterange: [new Date(new Date().getTime() - 3600 * 1000 * 2), new Date()],
|
dayDaterange: [new Date(new Date().getTime() - 3600 * 1000 * 2), new Date()],
|
||||||
},
|
},
|
||||||
@ -283,14 +291,14 @@ export default {
|
|||||||
devIsAllSelected() {
|
devIsAllSelected() {
|
||||||
if (this.devIdentifierList.length === 0) return false;
|
if (this.devIdentifierList.length === 0) return false;
|
||||||
const allIdentifiers = this.devIdentifierList.map(item => item.identifier);
|
const allIdentifiers = this.devIdentifierList.map(item => item.identifier);
|
||||||
return allIdentifiers.length > 0 &&
|
return allIdentifiers.length > 0 &&
|
||||||
allIdentifiers.every(id => this.devQueryParams.identifiers.includes(id));
|
allIdentifiers.every(id => this.devQueryParams.identifiers.includes(id));
|
||||||
},
|
},
|
||||||
// 判断场景变量是否全部选中
|
// 判断场景变量是否全部选中
|
||||||
sceneIsAllSelected() {
|
sceneIsAllSelected() {
|
||||||
if (this.sceneIdentifierList.length === 0) return false;
|
if (this.sceneIdentifierList.length === 0) return false;
|
||||||
const allIdentifiers = this.sceneIdentifierList.map(item => item.identifier);
|
const allIdentifiers = this.sceneIdentifierList.map(item => item.identifier);
|
||||||
return allIdentifiers.length > 0 &&
|
return allIdentifiers.length > 0 &&
|
||||||
allIdentifiers.every(id => this.sceneQueryParams.identifiers.includes(id));
|
allIdentifiers.every(id => this.sceneQueryParams.identifiers.includes(id));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -299,6 +307,7 @@ export default {
|
|||||||
getDevDeviceList() {
|
getDevDeviceList() {
|
||||||
const params = {
|
const params = {
|
||||||
showChild: true,
|
showChild: true,
|
||||||
|
entrance: 2,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 9999,
|
pageSize: 9999,
|
||||||
};
|
};
|
||||||
@ -311,7 +320,34 @@ export default {
|
|||||||
// 选择设备后
|
// 选择设备后
|
||||||
handleDevDeviceChange(val) {
|
handleDevDeviceChange(val) {
|
||||||
this.devQueryParams.identifiers = [];
|
this.devQueryParams.identifiers = [];
|
||||||
this.getDevIdentifierList(val);
|
this.devQueryParams.subDeviceId = null;
|
||||||
|
this.devSubDeviceList = [];
|
||||||
|
this.devIdentifierList = [];
|
||||||
|
const device = this.devDeviceList.find((item) => item.deviceId === val);
|
||||||
|
if (device && device.deviceType === 2) {
|
||||||
|
this.getDevSubDeviceList(val);
|
||||||
|
} else {
|
||||||
|
this.getDevIdentifierList(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 选择子设备后
|
||||||
|
handleDevSubDeviceChange(val) {
|
||||||
|
this.devQueryParams.identifiers = [];
|
||||||
|
this.devIdentifierList = [];
|
||||||
|
if (val) {
|
||||||
|
this.getDevIdentifierList(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取网关子设备列表
|
||||||
|
getDevSubDeviceList(gwDeviceId) {
|
||||||
|
listGateway({ gwDeviceId: gwDeviceId, pageNum: 1, pageSize: 9999 }).then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.devSubDeviceList = res.rows || [];
|
||||||
|
if (this.devSubDeviceList.length === 0) {
|
||||||
|
this.getDevIdentifierList(gwDeviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
// 设备变量选择变化
|
// 设备变量选择变化
|
||||||
handleDevIdentifierChange(val) {
|
handleDevIdentifierChange(val) {
|
||||||
@ -320,9 +356,9 @@ export default {
|
|||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
const allIdentifiers = this.devIdentifierList.map(item => item.identifier);
|
const allIdentifiers = this.devIdentifierList.map(item => item.identifier);
|
||||||
// 判断当前是否已经全部选中(基于当前值,不是新值)
|
// 判断当前是否已经全部选中(基于当前值,不是新值)
|
||||||
const isAllSelected = allIdentifiers.length > 0 &&
|
const isAllSelected = allIdentifiers.length > 0 &&
|
||||||
allIdentifiers.every(id => this.devQueryParams.identifiers.includes(id));
|
allIdentifiers.every(id => this.devQueryParams.identifiers.includes(id));
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (isAllSelected) {
|
if (isAllSelected) {
|
||||||
// 如果已经全部选中,则清空选择(反选)
|
// 如果已经全部选中,则清空选择(反选)
|
||||||
@ -342,21 +378,33 @@ export default {
|
|||||||
};
|
};
|
||||||
listThingsModel(params).then((res) => {
|
listThingsModel(params).then((res) => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.devIdentifierList = res.rows;
|
this.devIdentifierList = res.rows || [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 获取实际查询用的设备信息(子设备优先)
|
||||||
|
getQueryDevice() {
|
||||||
|
const device = this.devDeviceList.find((item) => item.deviceId === this.devQueryParams.deviceId);
|
||||||
|
if (!device) return null;
|
||||||
|
if (this.devSubDeviceList.length > 0 && this.devQueryParams.subDeviceId) {
|
||||||
|
const subDevice = this.devSubDeviceList.find((item) => item.subDeviceId === this.devQueryParams.subDeviceId);
|
||||||
|
if (subDevice) {
|
||||||
|
return { deviceId: subDevice.subDeviceId, serialNumber: subDevice.subDeviceNo };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { deviceId: device.deviceId, serialNumber: device.serialNumber };
|
||||||
|
},
|
||||||
// 获取设备图形数据
|
// 获取设备图形数据
|
||||||
getDevChartDatas() {
|
getDevChartDatas() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const devices = this.devDeviceList.find((item) => item.deviceId === this.devQueryParams.deviceId);
|
const queryDevice = this.getQueryDevice();
|
||||||
const identifierList = this.devQueryParams.identifiers.map((item) => {
|
const identifierList = this.devQueryParams.identifiers.map((item) => {
|
||||||
const identifiers = this.devIdentifierList.find((chil) => chil.identifier === item);
|
const identifiers = this.devIdentifierList.find((chil) => chil.identifier === item);
|
||||||
return { identifier: identifiers.identifier, type: identifiers.type };
|
return { identifier: identifiers.identifier, type: identifiers.type };
|
||||||
});
|
});
|
||||||
const params = {
|
const params = {
|
||||||
deviceId: devices.deviceId,
|
deviceId: queryDevice.deviceId,
|
||||||
serialNumber: devices.serialNumber,
|
serialNumber: queryDevice.serialNumber,
|
||||||
identifierList: identifierList,
|
identifierList: identifierList,
|
||||||
beginTime: moment(this.devQueryParams.dayDaterange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
beginTime: moment(this.devQueryParams.dayDaterange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
endTime: moment(this.devQueryParams.dayDaterange[1]).format('YYYY-MM-DD HH:mm:ss'),
|
endTime: moment(this.devQueryParams.dayDaterange[1]).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
@ -376,19 +424,24 @@ export default {
|
|||||||
},
|
},
|
||||||
// 搜索按钮操作
|
// 搜索按钮操作
|
||||||
handleDevQuery() {
|
handleDevQuery() {
|
||||||
const isField = this.areAllFields(this.devQueryParams);
|
if (!this.devQueryParams.deviceId || this.devQueryParams.identifiers.length === 0 || !this.devQueryParams.dayDaterange || this.devQueryParams.dayDaterange.length === 0) {
|
||||||
if (isField) {
|
return;
|
||||||
this.getDevChartDatas();
|
|
||||||
}
|
}
|
||||||
|
if (this.devSubDeviceList.length > 0 && !this.devQueryParams.subDeviceId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.getDevChartDatas();
|
||||||
},
|
},
|
||||||
// 重置按钮操作
|
// 重置按钮操作
|
||||||
handleDevResetQuery() {
|
handleDevResetQuery() {
|
||||||
this.resetForm('devQueryForm');
|
this.resetForm('devQueryForm');
|
||||||
this.devQueryParams.identifiers = [];
|
this.devQueryParams.identifiers = [];
|
||||||
|
this.devQueryParams.subDeviceId = null;
|
||||||
|
this.devSubDeviceList = [];
|
||||||
|
this.devIdentifierList = [];
|
||||||
this.devDatas = [];
|
this.devDatas = [];
|
||||||
this.devTableComTemp = [];
|
this.devTableComTemp = [];
|
||||||
this.devTotal = 0;
|
this.devTotal = 0;
|
||||||
this.handleDevQuery();
|
|
||||||
},
|
},
|
||||||
// 获取设备列表数据
|
// 获取设备列表数据
|
||||||
getDevTableList(e) {
|
getDevTableList(e) {
|
||||||
@ -436,9 +489,9 @@ export default {
|
|||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
const allIdentifiers = this.sceneIdentifierList.map(item => item.identifier);
|
const allIdentifiers = this.sceneIdentifierList.map(item => item.identifier);
|
||||||
// 判断当前是否已经全部选中(基于当前值,不是新值)
|
// 判断当前是否已经全部选中(基于当前值,不是新值)
|
||||||
const isAllSelected = allIdentifiers.length > 0 &&
|
const isAllSelected = allIdentifiers.length > 0 &&
|
||||||
allIdentifiers.every(id => this.sceneQueryParams.identifiers.includes(id));
|
allIdentifiers.every(id => this.sceneQueryParams.identifiers.includes(id));
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (isAllSelected) {
|
if (isAllSelected) {
|
||||||
// 如果已经全部选中,则清空选择(反选)
|
// 如果已经全部选中,则清空选择(反选)
|
||||||
@ -612,38 +665,38 @@ export default {
|
|||||||
},
|
},
|
||||||
// 导出设备历史数据
|
// 导出设备历史数据
|
||||||
async handleDevExport() {
|
async handleDevExport() {
|
||||||
const isField = this.areAllFields(this.devQueryParams);
|
if (!this.devQueryParams.deviceId || this.devQueryParams.identifiers.length === 0 || !this.devQueryParams.dayDaterange || this.devQueryParams.dayDaterange.length === 0) {
|
||||||
if (!isField) {
|
|
||||||
this.$message.warning('请先选择设备、变量和时间范围');
|
this.$message.warning('请先选择设备、变量和时间范围');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.devSubDeviceList.length > 0 && !this.devQueryParams.subDeviceId) {
|
||||||
|
this.$message.warning('请选择子设备');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.devDatas.length === 0) {
|
if (this.devDatas.length === 0) {
|
||||||
this.$message.warning('没有数据可导出');
|
this.$message.warning('没有数据可导出');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.exportLoading = true;
|
this.exportLoading = true;
|
||||||
const devices = this.devDeviceList.find((item) => item.deviceId === this.devQueryParams.deviceId);
|
const queryDevice = this.getQueryDevice();
|
||||||
const identifierList = this.devQueryParams.identifiers.map((item) => {
|
const identifierList = this.devQueryParams.identifiers.map((item) => {
|
||||||
const identifiers = this.devIdentifierList.find((chil) => chil.identifier === item);
|
const identifiers = this.devIdentifierList.find((chil) => chil.identifier === item);
|
||||||
return { identifier: identifiers.identifier, type: identifiers.type };
|
return { identifier: identifiers.identifier, type: identifiers.type };
|
||||||
});
|
});
|
||||||
const params = {
|
const params = {
|
||||||
deviceId: devices.deviceId,
|
deviceId: queryDevice.deviceId,
|
||||||
serialNumber: devices.serialNumber,
|
serialNumber: queryDevice.serialNumber,
|
||||||
identifierList: identifierList,
|
identifierList: identifierList,
|
||||||
beginTime: moment(this.devQueryParams.dayDaterange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
beginTime: moment(this.devQueryParams.dayDaterange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
endTime: moment(this.devQueryParams.dayDaterange[1]).format('YYYY-MM-DD HH:mm:ss'),
|
endTime: moment(this.devQueryParams.dayDaterange[1]).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
};
|
};
|
||||||
const res = await exportDeviceHistory(params);
|
const res = await exportDeviceHistory(params);
|
||||||
|
|
||||||
// 检查是否为Blob对象
|
|
||||||
if (res instanceof Blob) {
|
if (res instanceof Blob) {
|
||||||
// 验证是否为有效的blob(不是错误JSON)
|
|
||||||
const clonedBlob = res.slice();
|
const clonedBlob = res.slice();
|
||||||
const isValidBlob = await blobValidate(clonedBlob);
|
const isValidBlob = await blobValidate(clonedBlob);
|
||||||
if (!isValidBlob) {
|
if (!isValidBlob) {
|
||||||
// 可能是错误响应,尝试解析
|
|
||||||
try {
|
try {
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
const errorObj = JSON.parse(text);
|
const errorObj = JSON.parse(text);
|
||||||
@ -654,13 +707,11 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 有效的Excel文件,直接下载 - 使用设备编号作为文件名
|
const deviceSerialNumber = queryDevice.serialNumber || '设备数据';
|
||||||
const deviceSerialNumber = devices.serialNumber || '设备数据';
|
|
||||||
const fileName = `${deviceSerialNumber}_${moment().format('YYYYMMDDHHmmss')}.xlsx`;
|
const fileName = `${deviceSerialNumber}_${moment().format('YYYYMMDDHHmmss')}.xlsx`;
|
||||||
saveAs(res, fileName);
|
saveAs(res, fileName);
|
||||||
this.$message.success('导出成功');
|
this.$message.success('导出成功');
|
||||||
} else {
|
} else {
|
||||||
// 非Blob响应,可能是错误
|
|
||||||
this.$message.error('导出失败:服务器返回格式错误');
|
this.$message.error('导出失败:服务器返回格式错误');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@ -175,6 +175,17 @@
|
|||||||
<device-sub ref="deviceSub" :device="form"/>
|
<device-sub ref="deviceSub" :device="form"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- 子设备轮询任务管理(仅网关设备 + Modbus协议显示) -->
|
||||||
|
<el-tab-pane
|
||||||
|
name="gwSubModbusTask"
|
||||||
|
:disabled="form.deviceId == 0"
|
||||||
|
v-if="form.deviceType == 2 && (form.protocolCode == 'MODBUS-RTU' || form.protocolCode == 'MODBUS-TCP')"
|
||||||
|
lazy
|
||||||
|
>
|
||||||
|
<span slot="label">{{$t('device.device-edit.subDevicePollingManage')}}</span>
|
||||||
|
<gw-sub-modbus-task ref="gwSubModbusTask" :device="form"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane name="sipChannel" :disabled="form.deviceId == 0" v-if="form.deviceType === 3" lazy>
|
<el-tab-pane name="sipChannel" :disabled="form.deviceId == 0" v-if="form.deviceType === 3" lazy>
|
||||||
<span slot="label">{{ $t('device.device-edit.148398-44') }}</span>
|
<span slot="label">{{ $t('device.device-edit.148398-44') }}</span>
|
||||||
<channel ref="Channel" :device="form" @playerEvent="getPlayerData($event)"/>
|
<channel ref="Channel" :device="form" @playerEvent="getPlayerData($event)"/>
|
||||||
@ -375,6 +386,7 @@ import deviceInlineVideo from './device-inline-video';
|
|||||||
import deviceFuncLog from './device-functionlog';
|
import deviceFuncLog from './device-functionlog';
|
||||||
import deviceSub from './device-sub';
|
import deviceSub from './device-sub';
|
||||||
import deviceThingsModel from './device-things-model.vue';
|
import deviceThingsModel from './device-things-model.vue';
|
||||||
|
import gwSubModbusTask from './gw-sub-modbus-task';
|
||||||
import vueQr from 'vue-qr';
|
import vueQr from 'vue-qr';
|
||||||
import {loadMap} from '@/utils/map.js';
|
import {loadMap} from '@/utils/map.js';
|
||||||
import {
|
import {
|
||||||
@ -424,6 +436,7 @@ export default {
|
|||||||
deviceModbusTask,
|
deviceModbusTask,
|
||||||
deviceInlineVideo,
|
deviceInlineVideo,
|
||||||
deviceThingsModel,
|
deviceThingsModel,
|
||||||
|
gwSubModbusTask,
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
activeName(val) {
|
activeName(val) {
|
||||||
|
|||||||
@ -219,9 +219,9 @@ export default {
|
|||||||
},
|
},
|
||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
const deviceIds = row.deviceId || this.ids;
|
const deviceIds = row.id || this.ids;
|
||||||
this.$modal
|
this.$modal
|
||||||
.confirm(this.$t('device.device-sub.299018-26', [deviceIds]))
|
.confirm(this.$t('device.device-sub.299018-25', [deviceIds]))
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return delGateway(deviceIds);
|
return delGateway(deviceIds);
|
||||||
})
|
})
|
||||||
|
|||||||
260
yack-web/src/views/iot/device/gw-sub-modbus-task.vue
Normal file
260
yack-web/src/views/iot/device/gw-sub-modbus-task.vue
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<template>
|
||||||
|
<div class="gw-sub-modbus-task">
|
||||||
|
<el-row :gutter="12" style="height: 100%">
|
||||||
|
<!-- 左侧:子设备列表 -->
|
||||||
|
<el-col :span="6">
|
||||||
|
<div class="sub-device-panel">
|
||||||
|
<div class="panel-title">
|
||||||
|
<i class="el-icon-connection"></i>
|
||||||
|
<span>子设备列表</span>
|
||||||
|
<el-tag size="mini" type="info" style="margin-left: 8px">{{ subDeviceList.length }}</el-tag>
|
||||||
|
</div>
|
||||||
|
<div v-loading="listLoading" class="sub-device-list">
|
||||||
|
<div
|
||||||
|
v-for="item in subDeviceList"
|
||||||
|
:key="item.subDeviceId"
|
||||||
|
class="sub-device-item"
|
||||||
|
:class="{ active: selectedSubDevice && selectedSubDevice.deviceId === item.subDeviceId }"
|
||||||
|
@click="handleSelectSubDevice(item)"
|
||||||
|
>
|
||||||
|
<div class="sub-device-name">
|
||||||
|
<i class="el-icon-cpu"></i>
|
||||||
|
{{ item.subDeviceName || item.subDeviceNo }}
|
||||||
|
</div>
|
||||||
|
<div class="sub-device-meta">
|
||||||
|
<span class="meta-item">
|
||||||
|
<i class="el-icon-document"></i>
|
||||||
|
从机地址:{{ item.slaveId || '-' }}
|
||||||
|
</span>
|
||||||
|
<span class="meta-item">
|
||||||
|
<el-badge
|
||||||
|
:type="item.status === 3 ? 'success' : 'info'"
|
||||||
|
is-dot
|
||||||
|
style="margin-right: 4px"
|
||||||
|
/>
|
||||||
|
{{ item.status === 3 ? '在线' : '离线' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="sub-device-sn">{{ item.subDeviceNo }}</div>
|
||||||
|
</div>
|
||||||
|
<el-empty
|
||||||
|
v-if="!listLoading && subDeviceList.length === 0"
|
||||||
|
description="暂无子设备"
|
||||||
|
:image-size="80"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<!-- 右侧:轮询任务 -->
|
||||||
|
<el-col :span="18">
|
||||||
|
<div class="task-panel">
|
||||||
|
<div v-if="selectedSubDevice" class="task-panel-header">
|
||||||
|
<span class="selected-device-info">
|
||||||
|
<i class="el-icon-right" style="color: #409eff"></i>
|
||||||
|
当前设备:<strong>{{ selectedSubDevice.deviceName }}</strong>
|
||||||
|
<el-tag size="mini" style="margin-left: 8px">SN: {{ selectedSubDevice.serialNumber }}</el-tag>
|
||||||
|
<el-tag size="mini" type="warning" style="margin-left: 4px">从机地址: {{ selectedSubDevice.slaveId }}</el-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<device-modbus-task
|
||||||
|
v-if="selectedSubDevice"
|
||||||
|
ref="modbusTask"
|
||||||
|
:device="selectedSubDevice"
|
||||||
|
/>
|
||||||
|
<el-empty
|
||||||
|
v-else
|
||||||
|
description="请从左侧选择子设备以管理其轮询任务"
|
||||||
|
:image-size="120"
|
||||||
|
style="margin-top: 80px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listGateway } from '@/api/iot/gateway';
|
||||||
|
import deviceModbusTask from './device-modbus-task';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'gw-sub-modbus-task',
|
||||||
|
components: {
|
||||||
|
deviceModbusTask,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
device: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
listLoading: false,
|
||||||
|
subDeviceList: [],
|
||||||
|
selectedSubDevice: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
device: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal && newVal.deviceId && newVal.deviceId != 0) {
|
||||||
|
this.selectedSubDevice = null;
|
||||||
|
this.loadSubDeviceList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.device && this.device.deviceId && this.device.deviceId != 0) {
|
||||||
|
this.loadSubDeviceList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadSubDeviceList() {
|
||||||
|
this.listLoading = true;
|
||||||
|
listGateway({ gwDeviceId: this.device.deviceId, pageSize: 999 })
|
||||||
|
.then((response) => {
|
||||||
|
this.subDeviceList = response.rows || [];
|
||||||
|
// 默认选中第一个有从机地址的子设备
|
||||||
|
if (this.subDeviceList.length > 0 && !this.selectedSubDevice) {
|
||||||
|
this.handleSelectSubDevice(this.subDeviceList[0]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.listLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSelectSubDevice(item) {
|
||||||
|
// 构造 device-modbus-task 组件所需的 device 对象
|
||||||
|
this.selectedSubDevice = {
|
||||||
|
deviceId: item.subDeviceId,
|
||||||
|
deviceName: item.subDeviceName || item.subDeviceNo,
|
||||||
|
serialNumber: item.subDeviceNo,
|
||||||
|
productId: item.subProductId,
|
||||||
|
protocolCode: this.device.protocolCode,
|
||||||
|
deviceType: 4, // SUB_GATEWAY
|
||||||
|
slaveId: item.slaveId,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.gw-sub-modbus-task {
|
||||||
|
padding: 8px 0;
|
||||||
|
min-height: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-device-panel {
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 500px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-bottom: 1px solid #e4e7ed;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 6px;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-device-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-device-item {
|
||||||
|
padding: 10px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0f7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: #ecf5ff;
|
||||||
|
border-left-color: #409eff;
|
||||||
|
|
||||||
|
.sub-device-name {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-device-name {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-device-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
|
.meta-item {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #909399;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-device-sn {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #c0c4cc;
|
||||||
|
margin-top: 2px;
|
||||||
|
font-family: monospace;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-panel {
|
||||||
|
.task-panel-header {
|
||||||
|
padding: 8px 0 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
border-bottom: 1px dashed #e4e7ed;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.selected-device-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -356,6 +356,7 @@ export default {
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 12,
|
pageSize: 12,
|
||||||
showChild: true,
|
showChild: true,
|
||||||
|
entrance: 1,
|
||||||
deviceName: null,
|
deviceName: null,
|
||||||
productId: null,
|
productId: null,
|
||||||
groupId: null,
|
groupId: null,
|
||||||
@ -423,7 +424,7 @@ export default {
|
|||||||
if (pageNum != null) {
|
if (pageNum != null) {
|
||||||
this.queryParams.pageNum = Number(pageNum);
|
this.queryParams.pageNum = Number(pageNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
const time = this.$route.query.t;
|
const time = this.$route.query.t;
|
||||||
if (time != null && time != this.uniqueId) {
|
if (time != null && time != this.uniqueId) {
|
||||||
this.uniqueId = time;
|
this.uniqueId = time;
|
||||||
|
|||||||
@ -21,6 +21,9 @@
|
|||||||
<div>
|
<div>
|
||||||
{{ item.type == 0 ? $t('device.instruction-parsing.830424-1') : item.type == 1 ? $t('device.instruction-parsing.830424-2') : $t('device.instruction-parsing.830424-3') }}
|
{{ item.type == 0 ? $t('device.instruction-parsing.830424-1') : item.type == 1 ? $t('device.instruction-parsing.830424-2') : $t('device.instruction-parsing.830424-3') }}
|
||||||
</div>
|
</div>
|
||||||
|
<el-tag v-if="item.subDeviceName" size="mini" type="warning" style="margin-left: 8px;" :title="item.subSerialNumber">
|
||||||
|
{{ item.subDeviceName }} (从机{{ item.subSlaveId }})
|
||||||
|
</el-tag>
|
||||||
<div class="head-time">{{ item.time }}</div>
|
<div class="head-time">{{ item.time }}</div>
|
||||||
<div class="analysis-btn right-btn" v-if="item.type != 0 && !item.analysis" v-loading="item.loading" @click.stop="analysisData(item, index)">
|
<div class="analysis-btn right-btn" v-if="item.type != 0 && !item.analysis" v-loading="item.loading" @click.stop="analysisData(item, index)">
|
||||||
{{ $t('device.instruction-parsing.830424-4') }}
|
{{ $t('device.instruction-parsing.830424-4') }}
|
||||||
@ -534,6 +537,9 @@ export default {
|
|||||||
analysis: false,
|
analysis: false,
|
||||||
//解析的数据数组
|
//解析的数据数组
|
||||||
analysisList: data.message,
|
analysisList: data.message,
|
||||||
|
subDeviceName: data.subDeviceName || '',
|
||||||
|
subSerialNumber: data.subSerialNumber || '',
|
||||||
|
subSlaveId: data.subSlaveId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -54,7 +54,7 @@
|
|||||||
:disabled="!item.enabled"
|
:disabled="!item.enabled"
|
||||||
size="small"
|
size="small"
|
||||||
@change="onCountChange(index)"
|
@change="onCountChange(index)"
|
||||||
@input.native="onCountInput(index, $event)"
|
@keyup.native="onCountKeyup(index, $event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -194,10 +194,12 @@ export default {
|
|||||||
onCountChange(index) {
|
onCountChange(index) {
|
||||||
this.rebuildDevices(index);
|
this.rebuildDevices(index);
|
||||||
},
|
},
|
||||||
onCountInput(index, e) {
|
onCountKeyup(index, e) {
|
||||||
this.$nextTick(() => {
|
if (!e.target || e.target.tagName !== 'INPUT') return;
|
||||||
this.rebuildDevices(index);
|
const val = parseInt(e.target.value);
|
||||||
});
|
if (isNaN(val) || val < 1 || val > 10) return;
|
||||||
|
this.subProductList[index].count = val;
|
||||||
|
this.rebuildDevices(index);
|
||||||
},
|
},
|
||||||
rebuildDevices(index) {
|
rebuildDevices(index) {
|
||||||
const item = this.subProductList[index];
|
const item = this.subProductList[index];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user