20260117:纯计算物模型下沉到设备端
This commit is contained in:
parent
faa11dc37a
commit
d12016e04a
@ -129,7 +129,7 @@ emqx:
|
||||
ApiSecret: emqx.api.123
|
||||
# sip 配置
|
||||
sip:
|
||||
enabled: true # 是否启用视频监控SIP,true为启用
|
||||
enabled: false # 是否启用视频监控SIP,true为启用
|
||||
## 本地调试时,绑定网卡局域网IP,设备在同一局域网,设备接入IP填写绑定IP
|
||||
## 部署服务端时,默认绑定容器IP,设备接入IP填写服务器公网IP
|
||||
ip: 192.168.110.160 # 本服务启动的 IP
|
||||
|
||||
@ -239,10 +239,18 @@ public interface FastBeeConstant {
|
||||
String DEVICE_PRE_KEY = "TSLV:";
|
||||
|
||||
/**
|
||||
* 物模型缓存
|
||||
* 物模型缓存(产品级)
|
||||
* Key格式:TSL:{productId} hKey: identifier value: thingsModel
|
||||
*/
|
||||
String TSL_PRE_KEY = "TSL:";
|
||||
|
||||
/**
|
||||
* 设备级计算物模型缓存
|
||||
* Key格式:TSLD:{deviceId} hKey: identifier value: thingsModel
|
||||
* 用于存储设备级别的纯计算物模型,device_id > 0
|
||||
*/
|
||||
String TSLD_PRE_KEY = "TSLD:";
|
||||
|
||||
String MODBUS_PRE_KEY = "MODBUS:";
|
||||
|
||||
/**
|
||||
|
||||
@ -54,13 +54,21 @@ public class RedisKeyBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 物模型缓存key
|
||||
* 物模型缓存key(产品级)
|
||||
* 物模型命名空间:Key:TSL:{productId} hkey: identity value: thingsModel
|
||||
*/
|
||||
public static String buildTSLCacheKey(Long productId) {
|
||||
return FastBeeConstant.REDIS.TSL_PRE_KEY + productId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备级计算物模型缓存key
|
||||
* 物模型命名空间:Key:TSLD:{SerialNumber} hkey: identity value: thingsModel
|
||||
*/
|
||||
public static String buildDeviceTSLCacheKey(String serialNumber) {
|
||||
return FastBeeConstant.REDIS.TSLD_PRE_KEY + serialNumber;
|
||||
}
|
||||
|
||||
public static String buildModbusKey(Long productId) {
|
||||
return FastBeeConstant.REDIS.MODBUS_PRE_KEY + productId;
|
||||
}
|
||||
|
||||
@ -39,6 +39,11 @@ public class ThingsModelSimpleItem {
|
||||
**/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 是否纯计算型物模型
|
||||
*/
|
||||
private boolean isCalculated;
|
||||
|
||||
|
||||
public ThingsModelSimpleItem(String id, String value, String remark) {
|
||||
this.id = id;
|
||||
@ -88,4 +93,12 @@ public class ThingsModelSimpleItem {
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isCalculated() {
|
||||
return isCalculated;
|
||||
}
|
||||
|
||||
public void setCalculated(boolean calculated) {
|
||||
isCalculated = calculated;
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,11 +19,9 @@ import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.gateway.CRC16Utils;
|
||||
import com.fastbee.common.utils.modbus.BitUtils;
|
||||
import com.fastbee.helper.ModbusProtocolHelper;
|
||||
import com.fastbee.iot.cache.ICalculatedThingsModelCache;
|
||||
import com.fastbee.iot.cache.IModbusConfigCache;
|
||||
import com.fastbee.iot.domain.Device;
|
||||
import com.fastbee.iot.cache.ITSLCache;
|
||||
import com.fastbee.iot.domain.ModbusConfig;
|
||||
import com.fastbee.iot.mapper.DeviceMapper;
|
||||
import com.fastbee.iot.mapper.ModbusConfigMapper;
|
||||
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
|
||||
import com.fastbee.iot.service.ISubGatewayService;
|
||||
@ -40,7 +38,6 @@ import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -63,14 +60,12 @@ public class ModbusProtocol implements IProtocol {
|
||||
private IModbusConfigCache modbusConfigCache;
|
||||
@Resource
|
||||
private ISubGatewayService subGatewayService;
|
||||
@Resource
|
||||
private DeviceMapper deviceMapper;
|
||||
|
||||
@Resource
|
||||
private ModbusConfigMapper modbusConfigMapper;
|
||||
|
||||
@Resource
|
||||
private ICalculatedThingsModelCache calculatedThingsModelCache;
|
||||
private ITSLCache itslCache;
|
||||
|
||||
@Override
|
||||
public DeviceReport decode(DeviceData deviceData, String clientId) {
|
||||
@ -103,16 +98,16 @@ public class ModbusProtocol implements IProtocol {
|
||||
case Read01:
|
||||
case Read02:
|
||||
//起始地址
|
||||
String address0102 = getCacheModbusAddress(deviceData.getSerialNumber(), slaveId, code);
|
||||
String address0102 = getCacheModbusAddress(productId, slaveId, code);
|
||||
//读线圈或读离散型寄存器处理
|
||||
handleRead0102(productId, message, address0102, values);
|
||||
break;
|
||||
case Read03:
|
||||
case Read04:
|
||||
//起始地址
|
||||
String address0304 = getCacheModbusAddress(deviceData.getSerialNumber(), slaveId, code);
|
||||
String address0304 = getCacheModbusAddress(productId, slaveId, code);
|
||||
//读输入、保持寄存器
|
||||
handleRead0304(productId, message, address0304, values, hexDump);
|
||||
handleRead0304(deviceData.getSerialNumber(), productId, message, address0304, values, hexDump);
|
||||
break;
|
||||
case Write06:
|
||||
case Write05:
|
||||
@ -191,13 +186,14 @@ public class ModbusProtocol implements IProtocol {
|
||||
/**
|
||||
* 处理读保持寄存器/输入寄存器
|
||||
*
|
||||
* @param serialNumber 设备序列号
|
||||
* @param productId 产品id
|
||||
* @param message 数据
|
||||
* @param address 起始地址
|
||||
* @param simpleItemList 返回的物模型值
|
||||
* @param sourceStr 原始报文字符串
|
||||
*/
|
||||
private void handleRead0304(Long productId, ModbusRtu message, String address, List<ThingsModelSimpleItem> simpleItemList, String sourceStr) {
|
||||
private void handleRead0304(String serialNumber, Long productId, ModbusRtu message, String address, List<ThingsModelSimpleItem> simpleItemList, String sourceStr) {
|
||||
short[] data = message.getData();
|
||||
int length = data.length;
|
||||
if (length == 1) {
|
||||
@ -318,50 +314,35 @@ public class ModbusProtocol implements IProtocol {
|
||||
}
|
||||
|
||||
// 处理纯计算物模型:查询产品下所有纯计算物模型,构造 ThingsModelSimpleItem,值为 "0"
|
||||
handleCalculatedThingsModels(productId, simpleItemList, isReadOnly);
|
||||
handleCalculatedThingsModels(serialNumber, simpleItemList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理纯计算物模型
|
||||
* 从缓存获取纯计算物模型列表,构造 ThingsModelSimpleItem,值为 "0"
|
||||
* 后续在计算环节会统一处理所有物模型(包括纯计算物模型)
|
||||
* 从redis获取 纯计算物模型(设备级别物模型)
|
||||
*
|
||||
* @param productId 产品ID
|
||||
* @param serialNumber 设备编号
|
||||
* @param simpleItemList 物模型值列表
|
||||
* @param isReadOnly 是否只读
|
||||
*/
|
||||
private void handleCalculatedThingsModels(Long productId, List<ThingsModelSimpleItem> simpleItemList, int isReadOnly) {
|
||||
private void handleCalculatedThingsModels(String serialNumber, List<ThingsModelSimpleItem> simpleItemList) {
|
||||
|
||||
try {
|
||||
// 从缓存获取纯计算物模型标识符集合(支持产品级别和设备级别)
|
||||
Set<String> calculatedIdentifiers = calculatedThingsModelCache.getCalculatedIdentifiers(productId, null);
|
||||
|
||||
if (calculatedIdentifiers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取已上报的物模型标识符集合(避免重复)
|
||||
Set<String> reportedIdentifiers = simpleItemList.stream()
|
||||
.map(ThingsModelSimpleItem::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 为纯计算物模型构造 ThingsModelSimpleItem
|
||||
for (String identifier : calculatedIdentifiers) {
|
||||
// 跳过已上报的物模型
|
||||
if (reportedIdentifiers.contains(identifier)) {
|
||||
continue;
|
||||
}
|
||||
Map<String, ThingsModelValueItem> calculatedModelMap = itslCache.getDeviceCalculatedModelMap(serialNumber);
|
||||
|
||||
// 构造 ThingsModelSimpleItem,值为 "0"
|
||||
for (Map.Entry<String, ThingsModelValueItem> entry : calculatedModelMap.entrySet()) {
|
||||
String identifier = entry.getKey();
|
||||
|
||||
ThingsModelSimpleItem simpleItem = new ThingsModelSimpleItem();
|
||||
simpleItem.setId(identifier);
|
||||
simpleItem.setValue("0"); // 初始值设为0,后续在计算环节会重新计算
|
||||
simpleItem.setTs(DateUtils.getNowDate());
|
||||
simpleItem.setCalculated(true);
|
||||
simpleItemList.add(simpleItem);
|
||||
log.debug("=>添加纯计算物模型到上报列表,产品ID:[{}],标识符:[{}]", productId, identifier);
|
||||
log.debug("=>添加纯计算物模型到上报列表,设备:[{}],标识符:[{}]", serialNumber, identifier);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("=>处理纯计算物模型时发生错误,产品ID:[{}],错误:{}", productId, e.getMessage(), e);
|
||||
log.error("=>处理纯计算物模型时发生错误,设备:[{}],错误:{}", serialNumber, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,9 +465,9 @@ public class ModbusProtocol implements IProtocol {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取modbus地址
|
||||
* 获取寄存器最小modbus地址,从最小的寄存器地址开始解码
|
||||
*/
|
||||
private String getCacheModbusAddress(String serialNumber, int salveId, int code) {
|
||||
private String getCacheModbusAddress(Long productId, int salveId, int code) {
|
||||
// String key = RedisKeyBuilder.buildModbusRuntimeCacheKey(serialNumber);
|
||||
// Set<String> commandSet = redisCache.zRange(key, 0, -1);
|
||||
//
|
||||
@ -502,13 +483,14 @@ public class ModbusProtocol implements IProtocol {
|
||||
// log.warn("解析指令异常: {}", e.getMessage());
|
||||
// }
|
||||
// }
|
||||
// todo 拿缓存
|
||||
|
||||
// 2. 无匹配时查询数据库
|
||||
try {
|
||||
Device device = deviceMapper.selectDeviceBySerialNumber(serialNumber);
|
||||
if (device == null) return "0";
|
||||
// Device device = deviceMapper.selectDeviceBySerialNumber(serialNumber);
|
||||
// if (device == null) return "0";
|
||||
|
||||
List<ModbusConfig> configs = modbusConfigMapper.selectModbusConfigListByProductId(device.getProductId());
|
||||
List<ModbusConfig> configs = modbusConfigMapper.selectModbusConfigListByProductId(productId);
|
||||
if (CollectionUtils.isEmpty(configs)) return "0";
|
||||
|
||||
return configs.stream()
|
||||
|
||||
@ -125,12 +125,13 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
|
||||
if (minDelim != null) {
|
||||
int minDelimLength = minDelim.value.length;
|
||||
ByteBuf frame = null;
|
||||
boolean keepDelimiterForNextFrame = shouldKeepDelimiterForNextFrame(minDelim) && minFrameLength > 0;
|
||||
|
||||
if (discardingTooLongFrame) {
|
||||
// 如果true,将长度不符合报文丢弃
|
||||
// 初始化原来的值
|
||||
discardingTooLongFrame = false;
|
||||
buffer.skipBytes(minFrameLength + minDelimLength);
|
||||
buffer.skipBytes(minFrameLength + (keepDelimiterForNextFrame ? 0 : minDelimLength));
|
||||
|
||||
int tooLongFrameLength = this.tooLongFrameLength;
|
||||
this.tooLongFrameLength = 0;
|
||||
@ -142,10 +143,16 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
|
||||
/*小于最小长度帧处理*/
|
||||
if (minFrameLength > maxFrameLength) {
|
||||
//放弃读取帧
|
||||
buffer.skipBytes(minFrameLength + minDelimLength);
|
||||
buffer.skipBytes(minFrameLength + (keepDelimiterForNextFrame ? 0 : minDelimLength));
|
||||
fail(minFrameLength);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (keepDelimiterForNextFrame) {
|
||||
frame = buffer.readRetainedSlice(minFrameLength);
|
||||
return frame;
|
||||
}
|
||||
|
||||
/*是否需要跳过某字节*/
|
||||
if (minDelim.strip) {
|
||||
//忽略长度等于0的报文
|
||||
@ -183,6 +190,15 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 7E 分隔符需要保留给下一帧作为帧头使用(仅在存在前置数据时)。
|
||||
*/
|
||||
private boolean shouldKeepDelimiterForNextFrame(Delimiter delimiter) {
|
||||
return !delimiter.strip
|
||||
&& delimiter.value.length == 1
|
||||
&& (delimiter.value[0] & 0xFF) == 0x7E;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测7E帧格式(注册包/心跳包)
|
||||
* 注册包格式:7E(1) + 80(1) + MAC地址(6) + 7E(1) = 9字节
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
package com.fastbee.iot.cache;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 纯计算物模型缓存接口
|
||||
* 支持产品级别和设备级别的缓存
|
||||
* 支持本地缓存和 Redis 缓存的切换
|
||||
*
|
||||
* @author Sumuxi
|
||||
* @date 2026-01-02
|
||||
*/
|
||||
public interface ICalculatedThingsModelCache {
|
||||
|
||||
/**
|
||||
* 获取产品下的纯计算物模型标识符集合
|
||||
* @param productId 产品ID
|
||||
* @return 纯计算物模型标识符集合
|
||||
*/
|
||||
Set<String> getCalculatedIdentifiersByProduct(Long productId);
|
||||
|
||||
/**
|
||||
* 获取设备下的纯计算物模型标识符集合
|
||||
* 如果设备有自定义物模型,返回设备级别的;否则返回产品级别的
|
||||
* @param productId 产品ID
|
||||
* @param deviceId 设备ID(可选,未来扩展)
|
||||
* @return 纯计算物模型标识符集合
|
||||
*/
|
||||
Set<String> getCalculatedIdentifiers(Long productId, Long deviceId);
|
||||
|
||||
/**
|
||||
* 设置产品下的纯计算物模型标识符集合
|
||||
* @param productId 产品ID
|
||||
* @param identifiers 纯计算物模型标识符集合
|
||||
*/
|
||||
void setCalculatedIdentifiersByProduct(Long productId, Set<String> identifiers);
|
||||
|
||||
/**
|
||||
* 刷新产品下的纯计算物模型缓存
|
||||
* @param productId 产品ID
|
||||
*/
|
||||
void refreshCalculatedCache(Long productId);
|
||||
|
||||
/**
|
||||
* 清除产品下的纯计算物模型缓存
|
||||
* @param productId 产品ID
|
||||
*/
|
||||
void clearCalculatedCache(Long productId);
|
||||
|
||||
/**
|
||||
* 判断是否使用 Redis 缓存
|
||||
* @return true-使用Redis,false-使用本地缓存
|
||||
*/
|
||||
boolean isRedisCacheEnabled();
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ public interface ITSLCache {
|
||||
* \"name\":\"光强\",\"order\":0,\"regId\":\"light\",\"type\":1}]}
|
||||
* </p>
|
||||
*/
|
||||
public String setCacheThingsModelByProductId(Long productId);
|
||||
String setCacheThingsModelByProductId(Long productId);
|
||||
|
||||
/**
|
||||
* 获取map物模型 --返回 Map<String, PropertyDto>
|
||||
@ -90,7 +90,7 @@ public interface ITSLCache {
|
||||
* value:PropertyDto
|
||||
* </p>
|
||||
*/
|
||||
public Map<String, ThingsModelValueItem> setCacheThMapByProductId(Long productId);
|
||||
Map<String, ThingsModelValueItem> setCacheThMapByProductId(Long productId);
|
||||
|
||||
/**
|
||||
* 获取单个物模型缓存
|
||||
@ -99,6 +99,40 @@ public interface ITSLCache {
|
||||
* @param identify 标识符
|
||||
* @return PropertyDto
|
||||
*/
|
||||
public ThingsModelValueItem getSingleThingModels(Long productId, String identify);
|
||||
ThingsModelValueItem getSingleThingModels(Long productId, String identify);
|
||||
|
||||
// ==================== 设备级计算物模型缓存方法 ====================
|
||||
|
||||
/**
|
||||
* 获取设备级计算物模型缓存(Map形式)
|
||||
*
|
||||
* @param serialNumber 设备ID
|
||||
* @return Map<标识符, ThingsModelValueItem>
|
||||
*/
|
||||
Map<String, ThingsModelValueItem> getDeviceCalculatedModelMap(String serialNumber);
|
||||
|
||||
/**
|
||||
* 刷新设备级计算物模型缓存
|
||||
*
|
||||
* @param serialNumber 设备ID
|
||||
* @return Map<标识符, ThingsModelValueItem>
|
||||
*/
|
||||
Map<String, ThingsModelValueItem> refreshDeviceCalculatedModelCache(String serialNumber);
|
||||
|
||||
/**
|
||||
* 清除设备级计算物模型缓存
|
||||
*
|
||||
* @param serialNumber 设备ID
|
||||
*/
|
||||
void clearDeviceCalculatedModelCache(String serialNumber);
|
||||
|
||||
/**
|
||||
* 获取单个设备级计算物模型
|
||||
*
|
||||
* @param serialNumber 设备ID
|
||||
* @param identifier 标识符
|
||||
* @return ThingsModelValueItem
|
||||
*/
|
||||
ThingsModelValueItem getSingleDeviceCalculatedModel(String serialNumber, String identifier);
|
||||
|
||||
}
|
||||
|
||||
@ -1,149 +0,0 @@
|
||||
package com.fastbee.iot.cache.impl;
|
||||
|
||||
import com.fastbee.iot.cache.ICalculatedThingsModelCache;
|
||||
import com.fastbee.iot.domain.ThingsModel;
|
||||
import com.fastbee.iot.service.IThingsModelService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 纯计算物模型缓存实现(本地缓存)
|
||||
* 使用 ConcurrentHashMap 维护本地缓存
|
||||
* 适用于计算型物模型数量较少、单机部署的场景
|
||||
*
|
||||
* @author sumuxi
|
||||
* @date 2026/01/02
|
||||
*/
|
||||
@Service("calculatedThingsModelCache")
|
||||
@ConditionalOnProperty(name = "fastbee.cache.calculated-things-model.use-redis", havingValue = "false", matchIfMissing = true)
|
||||
@Slf4j
|
||||
public class CalculatedThingsModelCacheImpl implements ICalculatedThingsModelCache {
|
||||
|
||||
/**
|
||||
* 本地缓存:productId -> Set<String>(纯计算物模型标识符集合)
|
||||
*/
|
||||
private final ConcurrentHashMap<Long, Set<String>> calculatedCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 设备级别缓存:deviceId -> Set<String>(未来扩展)
|
||||
* 当设备有自定义物模型选择时使用
|
||||
*/
|
||||
private final ConcurrentHashMap<Long, Set<String>> deviceCalculatedCache = new ConcurrentHashMap<>();
|
||||
|
||||
@Resource
|
||||
private IThingsModelService thingsModelService;
|
||||
|
||||
/**
|
||||
* 是否启用 Redis 缓存(通过配置控制)
|
||||
* 默认 false,使用本地缓存
|
||||
* 当计算型物模型数量较多或需要集群部署时,设置为 true
|
||||
*/
|
||||
@Value("${fastbee.cache.calculated-things-model.use-redis:false}")
|
||||
private boolean useRedisCache;
|
||||
|
||||
/**
|
||||
* 本地缓存大小阈值
|
||||
* 超过此阈值建议切换到 Redis 缓存
|
||||
*/
|
||||
@Value("${fastbee.cache.calculated-things-model.local-cache-threshold:1000}")
|
||||
private int localCacheThreshold;
|
||||
|
||||
@Override
|
||||
public Set<String> getCalculatedIdentifiersByProduct(Long productId) {
|
||||
if (productId == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
// 从本地缓存获取
|
||||
Set<String> identifiers = calculatedCache.get(productId);
|
||||
if (identifiers != null) {
|
||||
return identifiers;
|
||||
}
|
||||
|
||||
// 缓存未命中,从数据库加载
|
||||
refreshCalculatedCache(productId);
|
||||
return calculatedCache.getOrDefault(productId, Collections.emptySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getCalculatedIdentifiers(Long productId, Long deviceId) {
|
||||
// 如果设备有自定义物模型选择,优先返回设备级别的
|
||||
if (deviceId != null) {
|
||||
Set<String> deviceIdentifiers = deviceCalculatedCache.get(deviceId);
|
||||
if (deviceIdentifiers != null) {
|
||||
return deviceIdentifiers;
|
||||
}
|
||||
}
|
||||
|
||||
// 否则返回产品级别的
|
||||
return getCalculatedIdentifiersByProduct(productId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCalculatedIdentifiersByProduct(Long productId, Set<String> identifiers) {
|
||||
if (productId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查缓存大小,超过阈值时记录警告
|
||||
if (calculatedCache.size() > localCacheThreshold) {
|
||||
log.warn("=>纯计算物模型本地缓存大小超过阈值[{}],建议切换到Redis缓存", localCacheThreshold);
|
||||
}
|
||||
|
||||
calculatedCache.put(productId, identifiers != null ? identifiers : Collections.emptySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshCalculatedCache(Long productId) {
|
||||
if (productId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 查询产品下所有物模型
|
||||
ThingsModel queryModel = new ThingsModel();
|
||||
queryModel.setProductId(productId);
|
||||
List<ThingsModel> allThingsModels = thingsModelService.selectThingsModelList(queryModel);
|
||||
|
||||
if (CollectionUtils.isEmpty(allThingsModels)) {
|
||||
calculatedCache.put(productId, Collections.emptySet());
|
||||
return;
|
||||
}
|
||||
|
||||
// 筛选出纯计算物模型(isCalculated == 1)
|
||||
Set<String> calculatedIdentifiers = allThingsModels.stream()
|
||||
.filter(model -> model.getIsCalculated() != null && model.getIsCalculated() == 1)
|
||||
.map(ThingsModel::getIdentifier)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
calculatedCache.put(productId, calculatedIdentifiers);
|
||||
log.debug("=>刷新纯计算物模型本地缓存成功,产品ID:[{}],数量:[{}]", productId, calculatedIdentifiers.size());
|
||||
} catch (Exception e) {
|
||||
log.error("=>刷新纯计算物模型本地缓存失败,产品ID:[{}],错误:{}", productId, e.getMessage(), e);
|
||||
calculatedCache.put(productId, Collections.emptySet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCalculatedCache(Long productId) {
|
||||
if (productId != null) {
|
||||
calculatedCache.remove(productId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRedisCacheEnabled() {
|
||||
return useRedisCache;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,163 +0,0 @@
|
||||
/*
|
||||
package com.fastbee.iot.cache.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fastbee.common.core.redis.RedisCache;
|
||||
import com.fastbee.common.core.redis.RedisKeyBuilder;
|
||||
import com.fastbee.iot.cache.ICalculatedThingsModelCache;
|
||||
import com.fastbee.iot.domain.ThingsModel;
|
||||
import com.fastbee.iot.service.IThingsModelService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
*/
|
||||
/**
|
||||
* 纯计算物模型缓存实现(Redis缓存)
|
||||
* 使用 Redis 维护缓存,支持集群部署
|
||||
* 适用于计算型物模型数量较多、集群部署的场景
|
||||
*
|
||||
* @author Sumuxi
|
||||
* @date 2026-01-02
|
||||
*//*
|
||||
|
||||
@Service("calculatedThingsModelRedisCache")
|
||||
@ConditionalOnProperty(name = "fastbee.cache.calculated-things-model.use-redis", havingValue = "true")
|
||||
@Slf4j
|
||||
public class CalculatedThingsModelRedisCacheImpl implements ICalculatedThingsModelCache {
|
||||
|
||||
*/
|
||||
/**
|
||||
* 缓存过期时间(秒),默认 24 小时
|
||||
*//*
|
||||
|
||||
@Value("${fastbee.cache.calculated-things-model.redis-ttl:86400}")
|
||||
private long redisTtl;
|
||||
|
||||
@Resource
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Resource
|
||||
private IThingsModelService thingsModelService;
|
||||
|
||||
@Override
|
||||
public Set<String> getCalculatedIdentifiersByProduct(Long productId) {
|
||||
if (productId == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
String cacheKey = RedisKeyBuilder.buildCalculatedThingsModelKey(productId);
|
||||
String cacheValue = redisCache.getCacheObject(cacheKey);
|
||||
|
||||
if (cacheValue != null) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<String> result = JSON.parseObject(cacheValue, Set.class);
|
||||
return result != null ? result : Collections.emptySet();
|
||||
} catch (Exception e) {
|
||||
log.warn("=>解析纯计算物模型Redis缓存失败,产品ID:[{}],错误:{}", productId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存未命中,从数据库加载
|
||||
refreshCalculatedCache(productId);
|
||||
cacheValue = redisCache.getCacheObject(cacheKey);
|
||||
if (cacheValue != null) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<String> result = JSON.parseObject(cacheValue, Set.class);
|
||||
return result != null ? result : Collections.emptySet();
|
||||
} catch (Exception e) {
|
||||
log.warn("=>解析纯计算物模型Redis缓存失败,产品ID:[{}],错误:{}", productId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getCalculatedIdentifiers(Long productId, Long deviceId) {
|
||||
// 如果设备有自定义物模型选择,优先返回设备级别的
|
||||
if (deviceId != null) {
|
||||
String deviceCacheKey = RedisKeyBuilder.buildCalculatedThingsModelDeviceKey(deviceId);
|
||||
String deviceCacheValue = redisCache.getCacheObject(deviceCacheKey);
|
||||
if (deviceCacheValue != null) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<String> result = JSON.parseObject(deviceCacheValue, Set.class);
|
||||
return result != null ? result : Collections.emptySet();
|
||||
} catch (Exception e) {
|
||||
log.warn("=>解析设备纯计算物模型Redis缓存失败,设备ID:[{}],错误:{}", deviceId, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 否则返回产品级别的
|
||||
return getCalculatedIdentifiersByProduct(productId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCalculatedIdentifiersByProduct(Long productId, Set<String> identifiers) {
|
||||
if (productId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String cacheKey = RedisKeyBuilder.buildCalculatedThingsModelKey(productId);
|
||||
String cacheValue = JSON.toJSONString(identifiers != null ? identifiers : Collections.emptySet());
|
||||
redisCache.setCacheObject(cacheKey, cacheValue, (int) redisTtl, java.util.concurrent.TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshCalculatedCache(Long productId) {
|
||||
if (productId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 查询产品下所有物模型
|
||||
ThingsModel queryModel = new ThingsModel();
|
||||
queryModel.setProductId(productId);
|
||||
List<ThingsModel> allThingsModels = thingsModelService.selectThingsModelList(queryModel);
|
||||
|
||||
if (CollectionUtils.isEmpty(allThingsModels)) {
|
||||
setCalculatedIdentifiersByProduct(productId, Collections.emptySet());
|
||||
return;
|
||||
}
|
||||
|
||||
// 筛选出纯计算物模型(isCalculated == 1)
|
||||
Set<String> calculatedIdentifiers = allThingsModels.stream()
|
||||
.filter(model -> model.getIsCalculated() != null && model.getIsCalculated() == 1)
|
||||
.map(ThingsModel::getIdentifier)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
setCalculatedIdentifiersByProduct(productId, calculatedIdentifiers);
|
||||
log.debug("=>刷新纯计算物模型Redis缓存成功,产品ID:[{}],数量:[{}]", productId, calculatedIdentifiers.size());
|
||||
} catch (Exception e) {
|
||||
log.error("=>刷新纯计算物模型Redis缓存失败,产品ID:[{}],错误:{}", productId, e.getMessage(), e);
|
||||
setCalculatedIdentifiersByProduct(productId, Collections.emptySet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCalculatedCache(Long productId) {
|
||||
if (productId != null) {
|
||||
String cacheKey = RedisKeyBuilder.buildCalculatedThingsModelKey(productId);
|
||||
redisCache.deleteObject(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRedisCacheEnabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@ -76,6 +76,10 @@ public class ModbusConfigCacheImpl implements IModbusConfigCache {
|
||||
resultMap.put(entry.getKey() + "", valueStr);
|
||||
}
|
||||
String modbusKey = RedisKeyBuilder.buildModbusKey(productId);
|
||||
// 先删除缓存,再插入缓存——完成缓存更新
|
||||
if (redisCache.containsKey(modbusKey)) {
|
||||
redisCache.deleteObject(modbusKey);
|
||||
}
|
||||
redisCache.hashPutAll(modbusKey, resultMap);
|
||||
return listMap;
|
||||
}
|
||||
|
||||
@ -23,10 +23,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -159,13 +156,11 @@ public class TSLCacheImpl implements ITSLCache {
|
||||
/*组装成原格式数据*/
|
||||
Map<String, List<ThingsModelValueItem>> result = thingsModelMap.values().stream().map(x -> JSON.parseObject(x, ThingsModelValueItem.class))
|
||||
.collect(Collectors.groupingBy(dto -> ThingsModelType.getName(dto.getType())));
|
||||
if (result != null) {
|
||||
for (List<ThingsModelValueItem> itemList : result.values()) {
|
||||
for (ThingsModelValueItem item : itemList) {
|
||||
convertModelName(item, SecurityUtils.getLanguage());
|
||||
}
|
||||
}
|
||||
}
|
||||
String jsonString = JSON.toJSONString(result);
|
||||
Product product = new Product();
|
||||
product.setProductId(productId);
|
||||
@ -289,6 +284,7 @@ public class TSLCacheImpl implements ITSLCache {
|
||||
// 数据库查询物模型集合
|
||||
ThingsModel model = new ThingsModel();
|
||||
model.setProductId(productId);
|
||||
model.setIsCalculated(0); // 只查询产品级物模型
|
||||
model.setLimitValue(SecurityUtils.getLanguage());
|
||||
List<ThingsModel> thingsModels = thingsModelService.selectThingsModelList(model);
|
||||
//modbus配置参数整合到物模型
|
||||
@ -325,12 +321,132 @@ public class TSLCacheImpl implements ITSLCache {
|
||||
|
||||
/*缓存到redis*/
|
||||
String cacheKey = RedisKeyBuilder.buildTSLCacheKey(productId);
|
||||
//先删除缓存
|
||||
// 先删除缓存
|
||||
if (redisCache.containsKey(cacheKey)) {
|
||||
redisCache.deleteObject(cacheKey);
|
||||
}
|
||||
// 再插入缓存——完成缓存更新
|
||||
redisCache.hashPutAll(cacheKey, thingsModelMap);
|
||||
return thingsModelMap;
|
||||
}
|
||||
|
||||
// ==================== 设备级计算物模型缓存方法 ====================
|
||||
|
||||
/**
|
||||
* 获取设备级计算物模型缓存(Map形式)
|
||||
*
|
||||
* @param serialNumber 设备ID
|
||||
* @return Map<标识符, ThingsModelValueItem>
|
||||
*/
|
||||
@Override
|
||||
public Map<String, ThingsModelValueItem> getDeviceCalculatedModelMap(String serialNumber) {
|
||||
if (StringUtils.isEmpty(serialNumber)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String cacheKey = RedisKeyBuilder.buildDeviceTSLCacheKey(serialNumber);
|
||||
Map<String, Object> map = redisCache.getCacheMap(cacheKey);
|
||||
if (!CollectionUtils.isEmpty(map)) {
|
||||
Map<String, ThingsModelValueItem> itemMap = map.values().stream()
|
||||
.map(v -> JSON.parseObject(v.toString(), ThingsModelValueItem.class))
|
||||
.collect(Collectors.toMap(ThingsModelValueItem::getId, Function.identity()));
|
||||
for (ThingsModelValueItem item : itemMap.values()) {
|
||||
convertModelName(item, SecurityUtils.getLanguage());
|
||||
}
|
||||
return itemMap;
|
||||
}
|
||||
// 缓存未命中,从数据库加载
|
||||
return refreshDeviceCalculatedModelCache(serialNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新设备级计算物模型缓存
|
||||
*
|
||||
* @param serialNumber 设备ID
|
||||
* @return Map<标识符, ThingsModelValueItem>
|
||||
*/
|
||||
@Override
|
||||
public Map<String, ThingsModelValueItem> refreshDeviceCalculatedModelCache(String serialNumber) {
|
||||
if (StringUtils.isEmpty(serialNumber)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
// 从数据库查询设备级计算物模型
|
||||
ThingsModel model = new ThingsModel();
|
||||
model.setSerialNumber(serialNumber);
|
||||
model.setIsCalculated(1); // 查询设备级物模型
|
||||
List<ThingsModel> models = thingsModelService.selectThingsModelList(model);
|
||||
if (CollectionUtils.isEmpty(models)) {
|
||||
// 清除可能存在的旧缓存
|
||||
clearDeviceCalculatedModelCache(serialNumber);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
// 转换为缓存格式
|
||||
Map<String, String> cacheMap = models.stream().collect(Collectors.toMap(
|
||||
ThingsModel::getIdentifier,
|
||||
thingsModel -> {
|
||||
ThingsModelValueItem dto = new ThingsModelValueItem();
|
||||
BeanUtils.copyProperties(thingsModel, dto);
|
||||
dto.setDatatype(JSONObject.parseObject(thingsModel.getSpecs(), Datatype.class))
|
||||
.setId(thingsModel.getIdentifier())
|
||||
.setName(thingsModel.getModelName())
|
||||
.setName_zh_CN(thingsModel.getModelName_zh_CN())
|
||||
.setName_en_US(thingsModel.getModelName_en_US())
|
||||
.setOrder(thingsModel.getModelOrder())
|
||||
.setModelId(thingsModel.getModelId())
|
||||
.setFormula(thingsModel.getFormula())
|
||||
.setIsCalculated(thingsModel.getIsCalculated());
|
||||
return JSONObject.toJSONString(dto);
|
||||
}
|
||||
));
|
||||
|
||||
// 缓存到Redis——先删再写
|
||||
String cacheKey = RedisKeyBuilder.buildDeviceTSLCacheKey(serialNumber);
|
||||
if (redisCache.containsKey(cacheKey)) {
|
||||
redisCache.deleteObject(cacheKey);
|
||||
}
|
||||
redisCache.hashPutAll(cacheKey, cacheMap);
|
||||
|
||||
// 返回转换后的Map
|
||||
return cacheMap.values().stream()
|
||||
.map(v -> JSON.parseObject(v, ThingsModelValueItem.class))
|
||||
.collect(Collectors.toMap(ThingsModelValueItem::getId, Function.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除设备级计算物模型缓存
|
||||
*
|
||||
* @param serialNumber 设备ID
|
||||
*/
|
||||
@Override
|
||||
public void clearDeviceCalculatedModelCache(String serialNumber) {
|
||||
if (StringUtils.isNotEmpty(serialNumber)) {
|
||||
String cacheKey = RedisKeyBuilder.buildDeviceTSLCacheKey(serialNumber);
|
||||
redisCache.deleteObject(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个设备级计算物模型
|
||||
*
|
||||
* @param serialNumber 设备ID
|
||||
* @param identifier 标识符
|
||||
* @return ThingsModelValueItem
|
||||
*/
|
||||
@Override
|
||||
public ThingsModelValueItem getSingleDeviceCalculatedModel(String serialNumber, String identifier) {
|
||||
if (StringUtils.isEmpty(serialNumber) || StringUtils.isEmpty(identifier)) {
|
||||
return null;
|
||||
}
|
||||
String cacheKey = RedisKeyBuilder.buildDeviceTSLCacheKey(serialNumber);
|
||||
String itemStr = redisCache.getCacheMapValue(cacheKey, identifier);
|
||||
if (StringUtils.isEmpty(itemStr)) {
|
||||
// 尝试刷新缓存后再获取
|
||||
Map<String, ThingsModelValueItem> modelMap = getDeviceCalculatedModelMap(serialNumber);
|
||||
return modelMap.get(identifier);
|
||||
}
|
||||
ThingsModelValueItem item = JSONObject.parseObject(itemStr, ThingsModelValueItem.class);
|
||||
convertModelName(item, SecurityUtils.getLanguage());
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -43,8 +43,8 @@ public class TSLValueCacheImpl implements ITSLValueCache {
|
||||
public List<ValueItem> getCacheDeviceStatus(Long productId, String deviceNumber) {
|
||||
String key = RedisKeyBuilder.buildTSLVCacheKey(productId, deviceNumber);
|
||||
Map<String, String> map = redisCache.hashEntity(key);
|
||||
List<ValueItem> valueList = new ArrayList<>();
|
||||
if (map == null || map.size() == 0) {
|
||||
List<ValueItem> valueList;
|
||||
if (map == null || map.isEmpty()) {
|
||||
// 缓存设备状态(物模型值)到redis
|
||||
valueList = addCacheDeviceStatus(productId, deviceNumber);
|
||||
} else {
|
||||
|
||||
@ -63,6 +63,18 @@ public class ThingsModel extends BaseEntity {
|
||||
@ApiModelProperty("产品名称")
|
||||
private String productName;
|
||||
|
||||
/**
|
||||
* 设备ID(0表示产品级物模型,>0表示设备级计算物模型)
|
||||
*/
|
||||
@ApiModelProperty(value = "设备ID", notes = "0表示产品级物模型,>0表示设备级计算物模型")
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 设备编号
|
||||
*/
|
||||
@ApiModelProperty("设备编号")
|
||||
private String serialNumber;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
|
||||
@ -156,7 +156,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
@Value("${server.tcp.port}")
|
||||
private Long tcpPort;
|
||||
|
||||
|
||||
// select cache
|
||||
|
||||
/**
|
||||
@ -165,7 +164,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
* @param deviceId 设备主键
|
||||
* @return 设备
|
||||
*/
|
||||
// @Cacheable(cacheNames = "Device", key = "#root.methodName + ':' + #serialNumber", unless = "#result == null")
|
||||
// @Cacheable(cacheNames = "Device", key = "#root.methodName + ':' +
|
||||
// #serialNumber", unless = "#result == null")
|
||||
@Override
|
||||
public Device selectDeviceByDeviceId(Long deviceId) {
|
||||
Device device = deviceMapper.selectDeviceByDeviceId(deviceId);
|
||||
@ -174,7 +174,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
// redis中获取设备状态(物模型值)
|
||||
device.setThingsModelValue(JSONObject.toJSONString(list));
|
||||
}
|
||||
//查询关联的监控设备
|
||||
// 查询关联的监控设备
|
||||
SipRelation sipRelation = new SipRelation();
|
||||
sipRelation.setReDeviceId(deviceId);
|
||||
List<SipRelation> sipRelationList = sipRelationService.selectSipRelationList(sipRelation);
|
||||
@ -280,7 +280,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
*/
|
||||
@Caching(evict = {
|
||||
@CacheEvict(cacheNames = "Device", key = "'selectDeviceBySerialNumber:' + #device.getSerialNumber()"),
|
||||
// @CacheEvict(cacheNames = "Device", key = "'selectDeviceByDeviceId:' + #device.getSerialNumber()"),
|
||||
// @CacheEvict(cacheNames = "Device", key = "'selectDeviceByDeviceId:' +
|
||||
// #device.getSerialNumber()"),
|
||||
@CacheEvict(cacheNames = "Device", key = "'selectDeviceStatusAndTransportStatus:' + #device.getSerialNumber()"),
|
||||
@CacheEvict(cacheNames = "Device", key = "'selectProtocolBySerialNumber:' + #device.getSerialNumber()"),
|
||||
@CacheEvict(cacheNames = "Device", key = "'selectShortDeviceBySerialNumber:' + #device.getSerialNumber()")
|
||||
@ -312,7 +313,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
|
||||
@Caching(evict = {
|
||||
@CacheEvict(cacheNames = "Device", key = "'selectDeviceBySerialNumber:' + #device.getSerialNumber()"),
|
||||
// @CacheEvict(cacheNames = "Device", key = "'selectDeviceByDeviceId:' + #device.getSerialNumber()"),
|
||||
// @CacheEvict(cacheNames = "Device", key = "'selectDeviceByDeviceId:' +
|
||||
// #device.getSerialNumber()"),
|
||||
@CacheEvict(cacheNames = "Device", key = "'selectDeviceStatusAndTransportStatus:' + #device.getSerialNumber()"),
|
||||
@CacheEvict(cacheNames = "Device", key = "'selectProtocolBySerialNumber:' + #device.getSerialNumber()"),
|
||||
@CacheEvict(cacheNames = "Device", key = "'selectShortDeviceBySerialNumber:' + #device.getSerialNumber()")
|
||||
@ -331,7 +333,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
@CacheEvict(cacheNames = "Device", allEntries = true)
|
||||
@Override
|
||||
public AjaxResult deleteDeviceByDeviceId(Long deviceId) {
|
||||
//查询设备
|
||||
// 查询设备
|
||||
Device device = deviceMapper.selectDeviceByDeviceId(deviceId);
|
||||
SysUser user = getLoginUser().getUser();
|
||||
Long sceneUserId;
|
||||
@ -356,7 +358,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
// 设备下不能有场景联动
|
||||
List<SceneDeviceBindVO> sceneDeviceBindVOList = sceneDeviceMapper.listSceneDeviceBind(device.getSerialNumber());
|
||||
if (null != sceneDeviceBindVOList.stream().filter(s -> s.getUserId().equals(sceneUserId)).findAny().orElse(null)) {
|
||||
if (null != sceneDeviceBindVOList.stream().filter(s -> s.getUserId().equals(sceneUserId)).findAny()
|
||||
.orElse(null)) {
|
||||
return AjaxResult.error("删除失败,请先删除对应设备下的场景联动");
|
||||
}
|
||||
// 查绑定用户被分享用户配置的场景,需要把场景删掉
|
||||
@ -412,12 +415,12 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
String key = RedisKeyBuilder.buildTSLVCacheKey(device.getProductId(), device.getSerialNumber());
|
||||
redisCache.deleteObject(key);
|
||||
// 删除设备
|
||||
deviceMapper.deleteDeviceByDeviceIds(new Long[]{deviceId});
|
||||
deviceMapper.deleteDeviceByDeviceIds(new Long[] { deviceId });
|
||||
// redis中删除规则脚本
|
||||
String cacheKey = RedisKeyBuilder.buildDeviceMsgCacheKey(device.getSerialNumber());
|
||||
redisCache.deleteObject(cacheKey);
|
||||
// 删除设备
|
||||
deviceMapper.deleteDeviceByDeviceIds(new Long[]{deviceId});
|
||||
deviceMapper.deleteDeviceByDeviceIds(new Long[] { deviceId });
|
||||
// 删除tcp协议设备缓存
|
||||
String tcpKey1 = RedisKeyBuilder.buildModbusTcpCacheKey(device.getSerialNumber());
|
||||
String tcpKey2 = RedisKeyBuilder.buildModbusTcpRuntimeCacheKey(device.getSerialNumber());
|
||||
@ -445,7 +448,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO-2.3 查询设备统计信息
|
||||
*
|
||||
@ -486,7 +488,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
return deviceMapper.selectProductAuthenticate(model);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新设备的物模型
|
||||
*
|
||||
@ -497,33 +498,47 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
*/
|
||||
@Override
|
||||
@DSTransactional
|
||||
public List<ThingsModelSimpleItem> reportDeviceThingsModelValue(ThingsModelValuesInput input, int type, boolean isShadow) {
|
||||
String key = RedisKeyBuilder.buildTSLVCacheKey(input.getProductId(), input.getDeviceNumber());
|
||||
public List<ThingsModelSimpleItem> reportDeviceThingsModelValue(ThingsModelValuesInput input, int type,
|
||||
boolean isShadow) {
|
||||
String serialNumber = input.getDeviceNumber();
|
||||
String key = RedisKeyBuilder.buildTSLVCacheKey(input.getProductId(), serialNumber);
|
||||
Map<String, String> maps = new HashMap<>();
|
||||
List<ThingsModelSimpleItem> list = new ArrayList<>();
|
||||
|
||||
//属性存储集合
|
||||
// 属性存储集合
|
||||
List<DeviceLog> deviceLogList = new ArrayList<>();
|
||||
//指令存储集合
|
||||
// 指令存储集合
|
||||
List<FunctionLog> functionLogList = new ArrayList<>();
|
||||
//事件存储集合
|
||||
// 事件存储集合
|
||||
List<EventLog> eventLogList = new ArrayList<>();
|
||||
// 获取当前时间
|
||||
Date nowDate = DateUtils.getNowDate();
|
||||
for (ThingsModelSimpleItem item : input.getThingsModelValueRemarkItem()) {
|
||||
List<ThingsModelSimpleItem> thingsModelValueRemarkItem = input.getThingsModelValueRemarkItem();
|
||||
// 判断是否有纯计算物模型 有:需要获取设备级别物模型 无:直接使用产品级别物模型即可
|
||||
boolean isCalculated = thingsModelValueRemarkItem.stream().anyMatch(ThingsModelSimpleItem::isCalculated);
|
||||
Map<String, ThingsModelValueItem> calculatedModelMap = new HashMap<>();
|
||||
if (isCalculated) {
|
||||
calculatedModelMap = itslCache.getDeviceCalculatedModelMap(serialNumber);
|
||||
}
|
||||
for (ThingsModelSimpleItem item : thingsModelValueRemarkItem) {
|
||||
String identity = item.getId();
|
||||
String serialNumber = input.getDeviceNumber();
|
||||
if (identity.startsWith("array_")) {
|
||||
identity = identity.substring(9);
|
||||
}
|
||||
|
||||
ThingsModelValueItem thingModels = thingsModelService.getSingleThingModels(input.getProductId(), identity);
|
||||
ThingsModelValueItem thingModels;
|
||||
if (item.isCalculated()) {
|
||||
// itslCache.getSingleDeviceCalculatedModel(serialNumber, identity);
|
||||
thingModels = calculatedModelMap.get(identity);
|
||||
} else {
|
||||
thingModels = thingsModelService.getSingleThingModels(input.getProductId(), identity);
|
||||
}
|
||||
if (Objects.isNull(thingModels)) {
|
||||
log.warn("=>查询物模型为空[{}],标识符:[{}]", input.getProductId(), identity);
|
||||
continue; // 跳过当前物模型的处理
|
||||
}
|
||||
// 获取设备实时值
|
||||
ValueItem cacheIdentifier = itslValueCache.getCacheIdentifier(input.getProductId(), input.getDeviceNumber(), thingModels.getId());
|
||||
ValueItem cacheIdentifier = itslValueCache.getCacheIdentifier(input.getProductId(), serialNumber,
|
||||
thingModels.getId());
|
||||
if (Objects.isNull(cacheIdentifier)) {
|
||||
log.warn("=>查询物模型值为空[{}],标识符:[{}]", input.getProductId(), identity);
|
||||
}
|
||||
@ -531,9 +546,9 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
String value = item.getValue();
|
||||
item.setName(thingModels.getName());
|
||||
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 数据计算 -开始 ★★★★★★★★★★★★★★★★★★★★★★*/
|
||||
//有计算公式的,经过计算公式
|
||||
if (thingModels != null && thingModels.getFormula() != null && !"".equals(thingModels.getFormula())) {
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 数据计算 -开始 ★★★★★★★★★★★★★★★★★★★★★★ */
|
||||
// 有计算公式的,经过计算公式
|
||||
if (thingModels.getFormula() != null && StringUtils.isNotEmpty(thingModels.getFormula())) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
// %s 表示当前物模型的值(保持向后兼容)
|
||||
params.put("%s", value);
|
||||
@ -549,7 +564,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
try {
|
||||
// 优先从当前消息中查找
|
||||
String refValue = null;
|
||||
for (ThingsModelSimpleItem currentItem : input.getThingsModelValueRemarkItem()) {
|
||||
for (ThingsModelSimpleItem currentItem : thingsModelValueRemarkItem) {
|
||||
String currentId = currentItem.getId();
|
||||
if (currentId.startsWith("array_")) {
|
||||
currentId = currentId.substring(9);
|
||||
@ -564,9 +579,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (refValue == null) {
|
||||
ValueItem refValueItem = itslValueCache.getCacheIdentifier(
|
||||
input.getProductId(),
|
||||
input.getDeviceNumber(),
|
||||
refIdentifier
|
||||
);
|
||||
serialNumber,
|
||||
refIdentifier);
|
||||
if (refValueItem != null && StringUtils.isNotEmpty(refValueItem.getValue())) {
|
||||
refValue = refValueItem.getValue();
|
||||
}
|
||||
@ -590,15 +604,15 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
log.info("=>纯计算物模型结果[{}],标识符:[{}],结果:{}", input.getProductId(), identity, value);
|
||||
item.setValue(value);
|
||||
}
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 数据计算 -结束 ★★★★★★★★★★★★★★★★★★★★★★*/
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 数据计算 -结束 ★★★★★★★★★★★★★★★★★★★★★★ */
|
||||
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 处理数据 - 开始 ★★★★★★★★★★★★★★★★★★★★★★*/
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 处理数据 - 开始 ★★★★★★★★★★★★★★★★★★★★★★ */
|
||||
ValueItem valueItem = new ValueItem();
|
||||
valueItem.setId(identity);
|
||||
if (id.startsWith("array_")) {
|
||||
// 查询是否有缓存,如果没有先进行缓存
|
||||
if (!redisCache.containsKey(key)) {
|
||||
Device device = this.selectDeviceBySerialNumber(input.getDeviceNumber());
|
||||
Device device = this.selectDeviceBySerialNumber(serialNumber);
|
||||
this.selectDeviceByDeviceId(device.getDeviceId());
|
||||
}
|
||||
|
||||
@ -623,7 +637,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
valueItem.setShadow(String.join(",", shadows));
|
||||
}
|
||||
redisCache.setCacheMapValue(key, identity, JSONObject.toJSONString(valueItem));
|
||||
//maps.put(identity, JSONObject.toJSONString(valueItem));
|
||||
// maps.put(identity, JSONObject.toJSONString(valueItem));
|
||||
} else {
|
||||
if (isShadow) {
|
||||
valueItem.setShadow(value);
|
||||
@ -638,11 +652,11 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
maps.put(identity, JSONObject.toJSONString(valueItem));
|
||||
}
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 处理数据 - 结束 ★★★★★★★★★★★★★★★★★★★★★★*/
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 处理数据 - 结束 ★★★★★★★★★★★★★★★★★★★★★★ */
|
||||
|
||||
/*★★★★★★★★★★★★★★★★★★★★★★ 存储数据 - 开始 ★★★★★★★★★★★★★★★★★★★★★★*/
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 存储数据 - 开始 ★★★★★★★★★★★★★★★★★★★★★★ */
|
||||
ThingsModelType modelType = ThingsModelType.getType(thingModels.getType());
|
||||
Device device = this.selectDeviceBySerialNumber(input.getDeviceNumber());
|
||||
Device device = this.selectDeviceBySerialNumber(serialNumber);
|
||||
switch (modelType) {
|
||||
case PROP:
|
||||
if (1 == thingModels.getIsHistory()) {
|
||||
@ -670,7 +684,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
FunctionLog function = new FunctionLog();
|
||||
function.setCreateTime(nowDate);
|
||||
function.setFunValue(value);
|
||||
function.setSerialNumber(input.getDeviceNumber());
|
||||
function.setSerialNumber(serialNumber);
|
||||
function.setIdentify(id);
|
||||
function.setShowValue(value);
|
||||
// 属性获取
|
||||
@ -703,22 +717,20 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
redisCache.hashPutAll(key, maps);
|
||||
// 存储历史数据——批量
|
||||
if (!CollectionUtils.isEmpty(deviceLogList) && !isShadow) {
|
||||
// for (DeviceLog deviceLog : deviceLogList) {
|
||||
// logService.saveDeviceLog(deviceLog);
|
||||
// }
|
||||
// for (DeviceLog deviceLog : deviceLogList) {
|
||||
// logService.saveDeviceLog(deviceLog);
|
||||
// }
|
||||
logService.saveDeviceLogBatch(deviceLogList);
|
||||
}
|
||||
//指令存储,影子模式不存储
|
||||
// 指令存储,影子模式不存储
|
||||
if (!CollectionUtils.isEmpty(functionLogList) && !isShadow) {
|
||||
functionLogService.insertBatch(functionLogList);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(eventLogList)) {
|
||||
//事件存储
|
||||
// 事件存储
|
||||
eventLogService.saveBatch(eventLogList);
|
||||
}
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 存储数据 - 结束 ★★★★★★★★★★★★★★★★★★★★★★*/
|
||||
|
||||
// 注意:纯计算物模型已在 ModbusProtocol 中构造 ThingsModelSimpleItem(值为0)
|
||||
/* ★★★★★★★★★★★★★★★★★★★★★★ 存储数据 - 结束 ★★★★★★★★★★★★★★★★★★★★★★ */
|
||||
|
||||
return list;
|
||||
}
|
||||
@ -751,17 +763,17 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
*/
|
||||
@Override
|
||||
public List<Device> selectDeviceList(Device device) {
|
||||
// SysUser user = getLoginUser().getUser();
|
||||
// List<SysRole> roles = user.getRoles();
|
||||
// for (int i = 0; i < roles.size(); i++) {
|
||||
// if (roles.get(i).getRoleKey().equals("tenant")) {
|
||||
// // 租户查看产品下所有设备
|
||||
// device.setTenantId(user.getUserId());
|
||||
// } else if (roles.get(i).getRoleKey().equals("general")) {
|
||||
// // 用户查看自己设备
|
||||
// device.setUserId(user.getUserId());
|
||||
// }
|
||||
// }
|
||||
// SysUser user = getLoginUser().getUser();
|
||||
// List<SysRole> roles = user.getRoles();
|
||||
// for (int i = 0; i < roles.size(); i++) {
|
||||
// if (roles.get(i).getRoleKey().equals("tenant")) {
|
||||
// // 租户查看产品下所有设备
|
||||
// device.setTenantId(user.getUserId());
|
||||
// } else if (roles.get(i).getRoleKey().equals("general")) {
|
||||
// // 用户查看自己设备
|
||||
// device.setUserId(user.getUserId());
|
||||
// }
|
||||
// }
|
||||
return deviceMapper.selectDeviceList(device);
|
||||
}
|
||||
|
||||
@ -773,17 +785,17 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
*/
|
||||
@Override
|
||||
public List<Device> selectUnAuthDeviceList(Device device) {
|
||||
// SysUser user = getLoginUser().getUser();
|
||||
// List<SysRole> roles = user.getRoles();
|
||||
// for (int i = 0; i < roles.size(); i++) {
|
||||
// if (roles.get(i).getRoleKey().equals("tenant")) {
|
||||
// // 租户查看产品下所有设备
|
||||
// device.setTenantId(user.getUserId());
|
||||
// } else if (roles.get(i).getRoleKey().equals("general")) {
|
||||
// // 用户查看自己设备
|
||||
// device.setUserId(user.getUserId());
|
||||
// }
|
||||
// }
|
||||
// SysUser user = getLoginUser().getUser();
|
||||
// List<SysRole> roles = user.getRoles();
|
||||
// for (int i = 0; i < roles.size(); i++) {
|
||||
// if (roles.get(i).getRoleKey().equals("tenant")) {
|
||||
// // 租户查看产品下所有设备
|
||||
// device.setTenantId(user.getUserId());
|
||||
// } else if (roles.get(i).getRoleKey().equals("general")) {
|
||||
// // 用户查看自己设备
|
||||
// device.setUserId(user.getUserId());
|
||||
// }
|
||||
// }
|
||||
return deviceMapper.selectUnAuthDeviceList(device);
|
||||
}
|
||||
|
||||
@ -795,17 +807,17 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
*/
|
||||
@Override
|
||||
public List<Device> selectDeviceListByGroup(Device device) {
|
||||
// SysUser user = getLoginUser().getUser();
|
||||
// List<SysRole> roles = user.getRoles();
|
||||
// for (int i = 0; i < roles.size(); i++) {
|
||||
// if (roles.get(i).getRoleKey().equals("tenant")) {
|
||||
// // 租户查看产品下所有设备
|
||||
// device.setTenantId(user.getUserId());
|
||||
// } else if (roles.get(i).getRoleKey().equals("general")) {
|
||||
// // 用户查看自己设备
|
||||
// device.setUserId(user.getUserId());
|
||||
// }
|
||||
// }
|
||||
// SysUser user = getLoginUser().getUser();
|
||||
// List<SysRole> roles = user.getRoles();
|
||||
// for (int i = 0; i < roles.size(); i++) {
|
||||
// if (roles.get(i).getRoleKey().equals("tenant")) {
|
||||
// // 租户查看产品下所有设备
|
||||
// device.setTenantId(user.getUserId());
|
||||
// } else if (roles.get(i).getRoleKey().equals("general")) {
|
||||
// // 用户查看自己设备
|
||||
// device.setUserId(user.getUserId());
|
||||
// }
|
||||
// }
|
||||
return deviceMapper.selectDeviceListByGroup(device);
|
||||
}
|
||||
|
||||
@ -845,7 +857,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
SysUser user = getLoginUser().getUser();
|
||||
if (null != user.getDept()) {
|
||||
Long tenantId = user.getDept().getDeptUserId();
|
||||
serialNumberList = list.stream().filter(d -> tenantId.equals(d.getTenantId())).map(DeviceShortOutput::getSerialNumber).collect(Collectors.toList());
|
||||
serialNumberList = list.stream().filter(d -> tenantId.equals(d.getTenantId()))
|
||||
.map(DeviceShortOutput::getSerialNumber).collect(Collectors.toList());
|
||||
} else {
|
||||
serialNumberList = list.stream().map(DeviceShortOutput::getSerialNumber).collect(Collectors.toList());
|
||||
}
|
||||
@ -870,19 +883,42 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
@Override
|
||||
public DeviceShortOutput selectDeviceRunningStatusByDeviceId(Long deviceId) {
|
||||
DeviceShortOutput device = deviceMapper.selectDeviceRunningStatusByDeviceId(deviceId);
|
||||
JSONObject thingsModelObject = JSONObject.parseObject(itslCache.getCacheThingsModelByProductId(device.getProductId()));
|
||||
// 1. 获取产品级物模型
|
||||
JSONObject thingsModelObject = JSONObject
|
||||
.parseObject(itslCache.getCacheThingsModelByProductId(device.getProductId()));
|
||||
JSONArray properties = thingsModelObject.getJSONArray("properties");
|
||||
JSONArray functions = thingsModelObject.getJSONArray("functions");
|
||||
List<ValueItem> thingsModelValueItems = itslValueCache.getCacheDeviceStatus(device.getProductId(), device.getSerialNumber());
|
||||
List<ValueItem> thingsModelValueItems = itslValueCache.getCacheDeviceStatus(device.getProductId(),
|
||||
device.getSerialNumber());
|
||||
// 物模型转换赋值
|
||||
List<ThingsModelValueItem> thingsList = new ArrayList<>();
|
||||
//判断一下properties 和 functions是否为空, 否则报空指针
|
||||
// 判断一下properties 和 functions是否为空, 否则报空指针
|
||||
if (!CollectionUtils.isEmpty(properties)) {
|
||||
thingsList.addAll(convertJsonToThingsList(properties, thingsModelValueItems, 1));
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(functions)) {
|
||||
thingsList.addAll(convertJsonToThingsList(functions, thingsModelValueItems, 2));
|
||||
}
|
||||
|
||||
// 2. 获取设备级计算物模型并合并
|
||||
Map<String, ThingsModelValueItem> deviceCalculatedMap = itslCache
|
||||
.getDeviceCalculatedModelMap(device.getSerialNumber());
|
||||
if (!CollectionUtils.isEmpty(deviceCalculatedMap)) {
|
||||
for (ThingsModelValueItem item : deviceCalculatedMap.values()) {
|
||||
// 获取缓存值
|
||||
ValueItem valueItem = thingsModelValueItems.stream()
|
||||
.filter(v -> v.getId().equals(item.getId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (valueItem != null) {
|
||||
item.setValue(valueItem.getValue());
|
||||
item.setShadow(valueItem.getShadow());
|
||||
item.setTs(valueItem.getTs());
|
||||
}
|
||||
thingsList.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
device.setThingsModels(thingsList);
|
||||
return device;
|
||||
|
||||
@ -897,7 +933,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
* @return
|
||||
*/
|
||||
@Async
|
||||
public List<ThingsModelValueItem> convertJsonToThingsList(JSONArray jsonArray, List<ValueItem> thingsModelValues, Integer type) {
|
||||
public List<ThingsModelValueItem> convertJsonToThingsList(JSONArray jsonArray, List<ValueItem> thingsModelValues,
|
||||
Integer type) {
|
||||
List<ThingsModelValueItem> thingsModelList = new ArrayList<>();
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
ThingsModelValueItem thingsModel = new ThingsModelValueItem();
|
||||
@ -905,11 +942,15 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
JSONObject datatypeJson = thingsJson.getJSONObject("datatype");
|
||||
thingsModel.setId(thingsJson.getString("id"));
|
||||
thingsModel.setName(thingsJson.getString("name"));
|
||||
thingsModel.setIsMonitor(thingsJson.getInteger("isMonitor") == null ? 0 : thingsJson.getInteger("isMonitor"));
|
||||
thingsModel.setIsReadonly(thingsJson.getInteger("isReadonly") == null ? 0 : thingsJson.getInteger("isReadonly"));
|
||||
thingsModel
|
||||
.setIsMonitor(thingsJson.getInteger("isMonitor") == null ? 0 : thingsJson.getInteger("isMonitor"));
|
||||
thingsModel.setIsReadonly(
|
||||
thingsJson.getInteger("isReadonly") == null ? 0 : thingsJson.getInteger("isReadonly"));
|
||||
thingsModel.setIsChart(thingsJson.getInteger("isChart") == null ? 0 : thingsJson.getInteger("isChart"));
|
||||
thingsModel.setIsSharePerm(thingsJson.getInteger("isSharePerm") == null ? 0 : thingsJson.getInteger("isSharePerm"));
|
||||
thingsModel.setIsHistory(thingsJson.getInteger("isHistory") == null ? 0 : thingsJson.getInteger("isHistory"));
|
||||
thingsModel.setIsSharePerm(
|
||||
thingsJson.getInteger("isSharePerm") == null ? 0 : thingsJson.getInteger("isSharePerm"));
|
||||
thingsModel
|
||||
.setIsHistory(thingsJson.getInteger("isHistory") == null ? 0 : thingsJson.getInteger("isHistory"));
|
||||
thingsModel.setIsApp(thingsJson.getInteger("isApp") == null ? 0 : thingsJson.getInteger("isApp"));
|
||||
thingsModel.setOrder(thingsJson.getInteger("order") == null ? 0 : thingsJson.getInteger("order"));
|
||||
thingsModel.setType(type);
|
||||
@ -957,9 +998,10 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
* @param parentIdentifier 上级标识符
|
||||
* @return
|
||||
*/
|
||||
private Datatype convertJsonToDataType(JSONObject datatypeJson, List<ValueItem> thingsModelValues, Integer type, String parentIdentifier) {
|
||||
private Datatype convertJsonToDataType(JSONObject datatypeJson, List<ValueItem> thingsModelValues, Integer type,
|
||||
String parentIdentifier) {
|
||||
Datatype dataType = new Datatype();
|
||||
//有些物模型数据定义为空的情况兼容
|
||||
// 有些物模型数据定义为空的情况兼容
|
||||
if (datatypeJson == null) {
|
||||
return dataType;
|
||||
}
|
||||
@ -986,10 +1028,12 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
} else if (dataType.getType().equals("object")) {
|
||||
JSONArray jsonArray = JSON.parseArray(datatypeJson.getString("params"));
|
||||
// 物模型值过滤(parentId_开头)
|
||||
thingsModelValues = thingsModelValues.stream().filter(x -> x.getId().startsWith(parentIdentifier)).collect(Collectors.toList());
|
||||
thingsModelValues = thingsModelValues.stream().filter(x -> x.getId().startsWith(parentIdentifier))
|
||||
.collect(Collectors.toList());
|
||||
List<ThingsModelValueItem> thingsList = convertJsonToThingsList(jsonArray, thingsModelValues, type);
|
||||
// 排序
|
||||
thingsList = thingsList.stream().sorted(Comparator.comparing(ThingsModelValueItem::getOrder).reversed()).collect(Collectors.toList());
|
||||
thingsList = thingsList.stream().sorted(Comparator.comparing(ThingsModelValueItem::getOrder).reversed())
|
||||
.collect(Collectors.toList());
|
||||
dataType.setParams(thingsList);
|
||||
} else if (dataType.getType().equals("array")) {
|
||||
dataType.setArrayType(datatypeJson.getString("arrayType"));
|
||||
@ -998,10 +1042,12 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
// 对象数组
|
||||
JSONArray jsonArray = datatypeJson.getJSONArray("params");
|
||||
// 物模型值过滤(parentId_开头)
|
||||
thingsModelValues = thingsModelValues.stream().filter(x -> x.getId().startsWith(parentIdentifier)).collect(Collectors.toList());
|
||||
thingsModelValues = thingsModelValues.stream().filter(x -> x.getId().startsWith(parentIdentifier))
|
||||
.collect(Collectors.toList());
|
||||
List<ThingsModelValueItem> thingsList = convertJsonToThingsList(jsonArray, thingsModelValues, type);
|
||||
// 排序
|
||||
thingsList = thingsList.stream().sorted(Comparator.comparing(ThingsModelValueItem::getOrder).reversed()).collect(Collectors.toList());
|
||||
thingsList = thingsList.stream().sorted(Comparator.comparing(ThingsModelValueItem::getOrder).reversed())
|
||||
.collect(Collectors.toList());
|
||||
// 数组类型物模型里面对象赋值
|
||||
List<ThingsModel>[] arrayParams = new List[dataType.getArrayCount()];
|
||||
for (int i = 0; i < dataType.getArrayCount(); i++) {
|
||||
@ -1053,7 +1099,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
throw new ServiceException("设备编号:" + device.getSerialNumber() + " 已经存在,新增失败");
|
||||
}
|
||||
SysUser sysUser = getLoginUser().getUser();
|
||||
//添加设备
|
||||
// 添加设备
|
||||
device.setCreateTime(DateUtils.getNowDate());
|
||||
device.setTenantId(sysUser.getDept().getDeptUserId());
|
||||
device.setTenantName(sysUser.getDept().getDeptUserName());
|
||||
@ -1067,7 +1113,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
setLocation(user.getLoginIp(), device);
|
||||
deviceMapper.insertDevice(device);
|
||||
// 处理modbus产品
|
||||
if (FastBeeConstant.PROTOCOL.ModbusRtu.equals(product.getProtocolCode()) || FastBeeConstant.PROTOCOL.ModbusTcp.equals(product.getProtocolCode())) {
|
||||
if (FastBeeConstant.PROTOCOL.ModbusRtu.equals(product.getProtocolCode())
|
||||
|| FastBeeConstant.PROTOCOL.ModbusTcp.equals(product.getProtocolCode())) {
|
||||
this.handleModbusConfig(device, product);
|
||||
}
|
||||
// redis缓存设备默认状态(物模型值)
|
||||
@ -1104,15 +1151,18 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
return;
|
||||
}
|
||||
productSubGatewayList.sort(Comparator.comparing(ProductSubGatewayVO::getSlaveId));
|
||||
List<Long> subProductIdList = productSubGatewayList.stream().map(ProductSubGatewayVO::getSubProductId).collect(Collectors.toList());
|
||||
List<Long> subProductIdList = productSubGatewayList.stream().map(ProductSubGatewayVO::getSubProductId)
|
||||
.collect(Collectors.toList());
|
||||
LambdaQueryWrapper<ProductModbusJob> queryWrapper1 = new LambdaQueryWrapper<>();
|
||||
queryWrapper1.in(ProductModbusJob::getProductId, subProductIdList);
|
||||
List<ProductModbusJob> productModbusJobList = productModbusJobMapper.selectList(queryWrapper1);
|
||||
Map<Long, List<ProductModbusJob>> productModbusJobMap = productModbusJobList.stream().collect(Collectors.groupingBy(ProductModbusJob::getProductId));
|
||||
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.setDeviceName(
|
||||
productSubGatewayVO.getSubProductName() + "_" + productSubGatewayVO.getSlaveId());
|
||||
addDevice.setProductId(productSubGatewayVO.getSubProductId());
|
||||
addDevice.setProductName(productSubGatewayVO.getSubProductName());
|
||||
String serialNumber = this.generationDeviceNum(1);
|
||||
@ -1134,7 +1184,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
subGateway.setCreateBy(addDevice.getCreateBy());
|
||||
subGateway.setCreateTime(new Date());
|
||||
subGatewayMapper.insertGateway(subGateway);
|
||||
List<ProductModbusJob> productModbusList = productModbusJobMap.get(productSubGatewayVO.getSubProductId());
|
||||
List<ProductModbusJob> productModbusList = productModbusJobMap
|
||||
.get(productSubGatewayVO.getSubProductId());
|
||||
if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(productModbusList)) {
|
||||
for (ProductModbusJob productModbusJob : productModbusList) {
|
||||
ModbusJob modbusJob = new ModbusJob();
|
||||
@ -1164,12 +1215,12 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AjaxResult deviceRelateUser(DeviceRelateUserInput params) {
|
||||
Long userId = params.getUserId(); //终端用户id
|
||||
Long userId = params.getUserId(); // 终端用户id
|
||||
// 查询用户信息
|
||||
SysUser sysUser = userService.selectUserById(userId);
|
||||
for (DeviceNumberAndProductId item : params.getDeviceNumberAndProductIds()) {
|
||||
Device existDevice = deviceMapper.selectDeviceBySerialNumber(item.getDeviceNumber());
|
||||
//查看设备是否已经分配
|
||||
// 查看设备是否已经分配
|
||||
if (!Objects.isNull(existDevice)) {
|
||||
if (null != sysUser.getDeptId()) {
|
||||
return AjaxResult.error("租户不允许绑定已存在的设备,设备编号:" + item.getDeviceNumber());
|
||||
@ -1183,7 +1234,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
}
|
||||
// 先删除设备的所有用户
|
||||
//deviceUserMapper.deleteDeviceUserByDeviceId(new UserIdDeviceIdModel(null, existDevice.getDeviceId()));
|
||||
// deviceUserMapper.deleteDeviceUserByDeviceId(new UserIdDeviceIdModel(null,
|
||||
// existDevice.getDeviceId()));
|
||||
// 添加新的设备用户
|
||||
DeviceUser deviceUser = new DeviceUser();
|
||||
deviceUser.setUserId(sysUser.getUserId());
|
||||
@ -1265,13 +1317,14 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
@Override
|
||||
public ThingsModelShadow getDeviceShadowThingsModel(Device device) {
|
||||
// 物模型值
|
||||
List<ValueItem> thingsModelValueItems = itslValueCache.getCacheDeviceStatus(device.getProductId(), device.getSerialNumber());
|
||||
List<ValueItem> thingsModelValueItems = itslValueCache.getCacheDeviceStatus(device.getProductId(),
|
||||
device.getSerialNumber());
|
||||
ThingsModelShadow shadow = new ThingsModelShadow();
|
||||
// 查询出设置的影子值
|
||||
//List<ThingsModelValueItem> shadowList = new ArrayList<>();
|
||||
// List<ThingsModelValueItem> shadowList = new ArrayList<>();
|
||||
for (ValueItem item : thingsModelValueItems) {
|
||||
if (!item.getShadow().equals(item.getValue()) && !item.getShadow().isEmpty()) {
|
||||
//处理id
|
||||
// 处理id
|
||||
String id = item.getId();
|
||||
if (item.getShadow().contains(",")) {
|
||||
String[] shadows = item.getShadow().split(",");
|
||||
@ -1297,7 +1350,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
return shadow;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成设备唯一编号
|
||||
*
|
||||
@ -1308,7 +1360,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
// 设备编号:D + userId + 15位随机字母和数字
|
||||
SysUser user = getLoginUser().getUser();
|
||||
String number;
|
||||
//Hex随机字符串
|
||||
// Hex随机字符串
|
||||
if (type == 3) {
|
||||
number = toolService.generateRandomHex(12);
|
||||
} else {
|
||||
@ -1433,8 +1485,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
deviceUserMapper.insertDeviceUser(deviceUser);
|
||||
}
|
||||
// 更新设备用户信息 多租户版本不需要
|
||||
// device.setTenantId(device.getTenantId());
|
||||
// device.setTenantName(sysUser.getUserName());
|
||||
// device.setTenantId(device.getTenantId());
|
||||
// device.setTenantName(sysUser.getUserName());
|
||||
}
|
||||
device.setUpdateTime(DateUtils.getNowDate());
|
||||
if (deviceEntity.getActiveTime() == null || deviceEntity.getActiveTime().equals("")) {
|
||||
@ -1459,7 +1511,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
return deviceMapper.selectDevicesByProductId(productId, hasSub);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 批量更新设备状态
|
||||
*
|
||||
@ -1472,7 +1523,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (CollectionUtils.isEmpty(serialNumbers)) {
|
||||
return;
|
||||
}
|
||||
//设备离线
|
||||
// 设备离线
|
||||
if (DeviceStatus.OFFLINE.equals(status)) {
|
||||
deviceMapper.batchChangeOffline(serialNumbers);
|
||||
} else if (DeviceStatus.ONLINE.equals(status)) {
|
||||
@ -1514,21 +1565,28 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
String password;
|
||||
if (ProductAuthConstant.AUTHORIZE.equals(deviceMqttVO.getIsAuthorize())) {
|
||||
// 查询产品授权码
|
||||
List<ProductAuthorize> productAuthorizeList = productAuthorizeService.listByProductId(deviceMqttVO.getProductId());
|
||||
List<ProductAuthorize> productAuthorizeList = productAuthorizeService
|
||||
.listByProductId(deviceMqttVO.getProductId());
|
||||
if (CollectionUtils.isEmpty(productAuthorizeList)) {
|
||||
throw new ServiceException("产品已启用授权,获取设备授权码失败,请先配置授权码");
|
||||
}
|
||||
List<ProductAuthorize> collect = productAuthorizeList.stream().filter(p -> p.getProductId().equals(deviceMqttVO.getDeviceId())).collect(Collectors.toList());
|
||||
ProductAuthorize productAuthorize = CollectionUtils.isEmpty(collect) ? productAuthorizeList.get(0) : collect.get(0);
|
||||
List<ProductAuthorize> collect = productAuthorizeList.stream()
|
||||
.filter(p -> p.getProductId().equals(deviceMqttVO.getDeviceId())).collect(Collectors.toList());
|
||||
ProductAuthorize productAuthorize = CollectionUtils.isEmpty(collect) ? productAuthorizeList.get(0)
|
||||
: collect.get(0);
|
||||
password = deviceMqttVO.getMqttPassword() + "&" + productAuthorize.getAuthorizeCode();
|
||||
} else {
|
||||
password = deviceMqttVO.getMqttPassword();
|
||||
}
|
||||
String clientId = ProductAuthConstant.CLIENT_ID_AUTH_TYPE_SIMPLE + "&" + deviceMqttVO.getSerialNumber() + "&" + deviceMqttVO.getProductId() + "&" + deviceMqttVO.getTenantId();
|
||||
String clientId = ProductAuthConstant.CLIENT_ID_AUTH_TYPE_SIMPLE + "&" + deviceMqttVO.getSerialNumber() + "&"
|
||||
+ deviceMqttVO.getProductId() + "&" + deviceMqttVO.getTenantId();
|
||||
// 组装返回结果
|
||||
connectVO.setClientId(clientId).setUsername(deviceMqttVO.getMqttAccount()).setPasswd(password).setPort(brokerPort);
|
||||
connectVO.setSubscribeTopic("/" + deviceMqttVO.getProductId() + "/" + deviceMqttVO.getSerialNumber() + "/function/get");
|
||||
connectVO.setReportTopic("/" + deviceMqttVO.getProductId() + "/" + deviceMqttVO.getSerialNumber() + "/property/post");
|
||||
connectVO.setClientId(clientId).setUsername(deviceMqttVO.getMqttAccount()).setPasswd(password)
|
||||
.setPort(brokerPort);
|
||||
connectVO.setSubscribeTopic(
|
||||
"/" + deviceMqttVO.getProductId() + "/" + deviceMqttVO.getSerialNumber() + "/function/get");
|
||||
connectVO.setReportTopic(
|
||||
"/" + deviceMqttVO.getProductId() + "/" + deviceMqttVO.getSerialNumber() + "/property/post");
|
||||
return connectVO;
|
||||
}
|
||||
|
||||
@ -1548,7 +1606,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
*/
|
||||
private void setLocation(String ip, Device device) {
|
||||
String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
|
||||
//String address = "未知地址";
|
||||
// String address = "未知地址";
|
||||
// 内网不查询
|
||||
if (IpUtils.internalIp(ip)) {
|
||||
device.setNetworkAddress("内网IP");
|
||||
@ -1576,13 +1634,15 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
String BAIDU_URL = "http://api.map.baidu.com/geocoding/v3/";
|
||||
city = StrUtil.isEmpty(city) ? "湖南省长沙市岳麓区" : city;
|
||||
String encodedAddress = URLEncoder.encode(city, StandardCharsets.UTF_8.toString());
|
||||
String baiduResponse = HttpUtils.sendGet(BAIDU_URL, "address=" + encodedAddress + "&output=json&ak=" + baiduAK, Constants.UTF8);
|
||||
String baiduResponse = HttpUtils.sendGet(BAIDU_URL, "address=" + encodedAddress + "&output=json&ak=" + baiduAK,
|
||||
Constants.UTF8);
|
||||
if (!StringUtils.isEmpty(baiduResponse)) {
|
||||
JSONObject baiduObject = JSONObject.parseObject(baiduResponse);
|
||||
JSONObject location = baiduObject.getJSONObject("result").getJSONObject("location");
|
||||
device.setLongitude(location.getBigDecimal("lng"));
|
||||
device.setLatitude(location.getBigDecimal("lat"));
|
||||
System.out.println(device.getSerialNumber() + "- 设置经度:" + location.getBigDecimal("lng") + ",设置纬度:" + location.getBigDecimal("lat"));
|
||||
System.out.println(device.getSerialNumber() + "- 设置经度:" + location.getBigDecimal("lng") + ",设置纬度:"
|
||||
+ location.getBigDecimal("lat"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1597,7 +1657,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
return deviceMapper.getDeviceNumsByProductId(productId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重置设备状态
|
||||
*
|
||||
@ -1616,7 +1675,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (null == product) {
|
||||
return "导入失败,产品信息为空";
|
||||
}
|
||||
List<String> serialNumberList = deviceImportVOList.stream().map(DeviceImportVO::getSerialNumber).collect(Collectors.toList());
|
||||
List<String> serialNumberList = deviceImportVOList.stream().map(DeviceImportVO::getSerialNumber)
|
||||
.collect(Collectors.toList());
|
||||
List<String> oldSerialNumberList = deviceMapper.checkExistBySerialNumbers(serialNumberList);
|
||||
if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(oldSerialNumberList)) {
|
||||
return "以下设备编号(" + JSON.toJSONString(oldSerialNumberList) + ")已存在,请修改后重试";
|
||||
@ -1645,12 +1705,16 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
int result = deviceMapper.insertBatchDevice(deviceList);
|
||||
// 新增导入记录
|
||||
DeviceRecord deviceRecord = new DeviceRecord();
|
||||
deviceRecord.setOperateDeptId(loginUser.getDeptId()).setProductId(productId).setType(DeviceRecordTypeEnum.IMPORT.getType())
|
||||
.setTotal(deviceList.size()).setTenantId(loginUser.getUser().getDept().getDeptUserId()).setTenantName(loginUser.getUser().getDept().getDeptUserName());
|
||||
deviceRecord.setOperateDeptId(loginUser.getDeptId()).setProductId(productId)
|
||||
.setType(DeviceRecordTypeEnum.IMPORT.getType())
|
||||
.setTotal(deviceList.size()).setTenantId(loginUser.getUser().getDept().getDeptUserId())
|
||||
.setTenantName(loginUser.getUser().getDept().getDeptUserName());
|
||||
if (result > 0) {
|
||||
deviceRecord.setStatus(StatusEnum.SUCCESS.getStatus()).setSuccessQuantity(deviceList.size()).setFailQuantity(0);
|
||||
deviceRecord.setStatus(StatusEnum.SUCCESS.getStatus()).setSuccessQuantity(deviceList.size())
|
||||
.setFailQuantity(0);
|
||||
} else {
|
||||
deviceRecord.setStatus(StatusEnum.FAIL.getStatus()).setSuccessQuantity(0).setFailQuantity(deviceList.size());
|
||||
deviceRecord.setStatus(StatusEnum.FAIL.getStatus()).setSuccessQuantity(0)
|
||||
.setFailQuantity(deviceList.size());
|
||||
}
|
||||
deviceRecord.setCreateBy(loginUser.getUsername());
|
||||
deviceRecordMapper.insertDeviceRecord(deviceRecord);
|
||||
@ -1664,7 +1728,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (null == product) {
|
||||
return "导入失败,产品信息为空";
|
||||
}
|
||||
List<String> serialNumberList = deviceAssignmentVOS.stream().map(DeviceAssignmentVO::getSerialNumber).collect(Collectors.toList());
|
||||
List<String> serialNumberList = deviceAssignmentVOS.stream().map(DeviceAssignmentVO::getSerialNumber)
|
||||
.collect(Collectors.toList());
|
||||
List<String> oldSerialNumberList = deviceMapper.checkExistBySerialNumbers(serialNumberList);
|
||||
if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(oldSerialNumberList)) {
|
||||
return "以下设备编号(" + JSON.toJSONString(oldSerialNumberList) + ")已存在,请修改后重试";
|
||||
@ -1701,8 +1766,11 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
int result = deviceMapper.insertBatchDevice(deviceList);
|
||||
// 新增导入记录
|
||||
DeviceRecord deviceRecord = new DeviceRecord();
|
||||
deviceRecord.setOperateDeptId(loginUser.getDeptId()).setTargetDeptId(deptId).setTargetDeptId(deptId).setProductId(productId).setDistributeType(DeviceDistributeTypeEnum.IMPORT.getType())
|
||||
.setType(DeviceRecordTypeEnum.ASSIGNMENT.getType()).setTotal(deviceList.size()).setTenantId(loginUser.getUser().getDept().getDeptUserId()).setTenantName(loginUser.getUser().getDept().getDeptUserName());
|
||||
deviceRecord.setOperateDeptId(loginUser.getDeptId()).setTargetDeptId(deptId).setTargetDeptId(deptId)
|
||||
.setProductId(productId).setDistributeType(DeviceDistributeTypeEnum.IMPORT.getType())
|
||||
.setType(DeviceRecordTypeEnum.ASSIGNMENT.getType()).setTotal(deviceList.size())
|
||||
.setTenantId(loginUser.getUser().getDept().getDeptUserId())
|
||||
.setTenantName(loginUser.getUser().getDept().getDeptUserName());
|
||||
List<DeviceRecord> deviceRecordList = new ArrayList<>();
|
||||
if (result > 0) {
|
||||
deviceRecord.setStatus(StatusEnum.SUCCESS.getStatus());
|
||||
@ -1717,10 +1785,15 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
int insertRecordStatus = deviceRecordMapper.insertDeviceRecord(deviceRecord);
|
||||
for (Device device : deviceList) {
|
||||
DeviceRecord deviceRecord1 = new DeviceRecord();
|
||||
deviceRecord1.setOperateDeptId(loginUser.getDeptId()).setTargetDeptId(deptId).setProductId(productId).setDeviceId(device.getDeviceId())
|
||||
.setType(DeviceRecordTypeEnum.ASSIGNMENT_DETAIL.getType()).setParentId(deviceRecord.getId()).setSerialNumber(device.getSerialNumber())
|
||||
.setDistributeType(DeviceDistributeTypeEnum.IMPORT.getType()).setTenantId(loginUser.getUser().getDept().getDeptUserId()).setTenantName(loginUser.getUser().getDept().getDeptUserName());
|
||||
deviceRecord1.setStatus(insertRecordStatus > 0 ? StatusEnum.SUCCESS.getStatus() : StatusEnum.FAIL.getStatus());
|
||||
deviceRecord1.setOperateDeptId(loginUser.getDeptId()).setTargetDeptId(deptId).setProductId(productId)
|
||||
.setDeviceId(device.getDeviceId())
|
||||
.setType(DeviceRecordTypeEnum.ASSIGNMENT_DETAIL.getType()).setParentId(deviceRecord.getId())
|
||||
.setSerialNumber(device.getSerialNumber())
|
||||
.setDistributeType(DeviceDistributeTypeEnum.IMPORT.getType())
|
||||
.setTenantId(loginUser.getUser().getDept().getDeptUserId())
|
||||
.setTenantName(loginUser.getUser().getDept().getDeptUserName());
|
||||
deviceRecord1
|
||||
.setStatus(insertRecordStatus > 0 ? StatusEnum.SUCCESS.getStatus() : StatusEnum.FAIL.getStatus());
|
||||
deviceRecord1.setCreateBy(loginUser.getUsername());
|
||||
deviceRecordList.add(deviceRecord1);
|
||||
}
|
||||
@ -1750,11 +1823,14 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (null == sysUser) {
|
||||
return AjaxResult.error("机构管理员不存在");
|
||||
}
|
||||
List<Long> deviceIdList = Arrays.stream(deviceIds.split(",")).map(d -> Long.parseLong(d.trim())).distinct().collect(Collectors.toList());
|
||||
List<Long> deviceIdList = Arrays.stream(deviceIds.split(",")).map(d -> Long.parseLong(d.trim())).distinct()
|
||||
.collect(Collectors.toList());
|
||||
int result = deviceMapper.updateTenantIdByDeptIds(deptUserId, sysUser.getUserName(), deviceIdList);
|
||||
DeviceRecord deviceRecord = new DeviceRecord();
|
||||
deviceRecord.setOperateDeptId(user.getDeptId()).setTargetDeptId(deptId).setTargetDeptId(deptId).setDistributeType(DeviceDistributeTypeEnum.SELECT.getType())
|
||||
.setType(DeviceRecordTypeEnum.ASSIGNMENT.getType()).setTotal(deviceIdList.size()).setTenantId(user.getDept().getDeptUserId()).setTenantName(user.getDept().getDeptUserName());
|
||||
deviceRecord.setOperateDeptId(user.getDeptId()).setTargetDeptId(deptId).setTargetDeptId(deptId)
|
||||
.setDistributeType(DeviceDistributeTypeEnum.SELECT.getType())
|
||||
.setType(DeviceRecordTypeEnum.ASSIGNMENT.getType()).setTotal(deviceIdList.size())
|
||||
.setTenantId(user.getDept().getDeptUserId()).setTenantName(user.getDept().getDeptUserName());
|
||||
if (result > 0) {
|
||||
deviceRecord.setStatus(StatusEnum.SUCCESS.getStatus());
|
||||
deviceRecord.setSuccessQuantity(deviceIdList.size());
|
||||
@ -1773,8 +1849,10 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
deviceRecord1.setOperateDeptId(user.getDeptId()).setTargetDeptId(deptId).setDeviceId(device.getDeviceId())
|
||||
.setProductId(device.getProductId()).setSerialNumber(device.getSerialNumber())
|
||||
.setType(DeviceRecordTypeEnum.ASSIGNMENT_DETAIL.getType()).setParentId(deviceRecord.getId())
|
||||
.setDistributeType(DeviceDistributeTypeEnum.SELECT.getType()).setTenantId(user.getDept().getDeptUserId()).setTenantName(user.getDept().getDeptUserName());
|
||||
deviceRecord1.setStatus(insertRecordStatus > 0 ? StatusEnum.SUCCESS.getStatus() : StatusEnum.FAIL.getStatus());
|
||||
.setDistributeType(DeviceDistributeTypeEnum.SELECT.getType())
|
||||
.setTenantId(user.getDept().getDeptUserId()).setTenantName(user.getDept().getDeptUserName());
|
||||
deviceRecord1
|
||||
.setStatus(insertRecordStatus > 0 ? StatusEnum.SUCCESS.getStatus() : StatusEnum.FAIL.getStatus());
|
||||
deviceRecord1.setCreateBy(user.getUserName());
|
||||
deviceRecordList.add(deviceRecord1);
|
||||
}
|
||||
@ -1795,7 +1873,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (null == sysUser) {
|
||||
return AjaxResult.error("机构管理员不存在");
|
||||
}
|
||||
List<Long> deviceIdList = Arrays.stream(deviceIds.split(",")).map(d -> Long.parseLong(d.trim())).distinct().collect(Collectors.toList());
|
||||
List<Long> deviceIdList = Arrays.stream(deviceIds.split(",")).map(d -> Long.parseLong(d.trim())).distinct()
|
||||
.collect(Collectors.toList());
|
||||
int result = deviceMapper.updateTenantIdByDeptIds(deptUserId, sysUser.getUserName(), deviceIdList);
|
||||
List<DeviceRecord> deviceRecordList = new ArrayList<>();
|
||||
List<Device> deviceList = deviceMapper.selectDeviceListByDeviceIds(deviceIdList);
|
||||
@ -1803,7 +1882,9 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
DeviceRecord deviceRecord1 = new DeviceRecord();
|
||||
deviceRecord1.setOperateDeptId(deptId).setTargetDeptId(recoveryDeptId).setDeviceId(device.getDeviceId())
|
||||
.setProductId(device.getProductId()).setSerialNumber(device.getSerialNumber())
|
||||
.setType(DeviceRecordTypeEnum.RECOVERY.getType()).setTenantId(loginUser.getUser().getDept().getDeptUserId()).setTenantName(loginUser.getUser().getDept().getDeptUserName());
|
||||
.setType(DeviceRecordTypeEnum.RECOVERY.getType())
|
||||
.setTenantId(loginUser.getUser().getDept().getDeptUserId())
|
||||
.setTenantName(loginUser.getUser().getDept().getDeptUserName());
|
||||
deviceRecord1.setStatus(result > 0 ? StatusEnum.SUCCESS.getStatus() : StatusEnum.FAIL.getStatus());
|
||||
deviceRecord1.setCreateBy(loginUser.getUsername());
|
||||
deviceRecordList.add(deviceRecord1);
|
||||
@ -1833,16 +1914,16 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
return deviceMapper.selectDeviceByChannelId(channelId);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ThingsModelDTO> listThingsModel(Long deviceId) {
|
||||
DeviceShortOutput device = deviceMapper.selectDeviceRunningStatusByDeviceId(deviceId);
|
||||
if (null == device) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
//管理员可以下发,其他看指令控制
|
||||
// 管理员可以下发,其他看指令控制
|
||||
int canSend = getLoginUser().getUserId().equals(getLoginUser().getDeptUserId()) ? 1 : 0;
|
||||
List<ThingsModelDTO> list = thingsModelService.getCacheAndHandleArrayByProductId(device.getProductId(), device.getSerialNumber());
|
||||
List<ThingsModelDTO> list = thingsModelService.getCacheAndHandleArrayByProductId(device.getProductId(),
|
||||
device.getSerialNumber());
|
||||
for (ThingsModelDTO thingsModelDTO : list) {
|
||||
thingsModelDTO.setProductId(device.getProductId());
|
||||
thingsModelDTO.setSerialNumber(device.getSerialNumber());
|
||||
@ -1853,7 +1934,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
subGateway.setGwDeviceId(deviceId);
|
||||
List<SubDeviceListVO> subDeviceListVOList = subGatewayMapper.selectGatewayList(subGateway);
|
||||
for (SubDeviceListVO subDeviceListVO : subDeviceListVOList) {
|
||||
List<ThingsModelDTO> subList = thingsModelService.getCacheAndHandleArrayByProductId(subDeviceListVO.getSubProductId(), subDeviceListVO.getSubDeviceNo());
|
||||
List<ThingsModelDTO> subList = thingsModelService.getCacheAndHandleArrayByProductId(
|
||||
subDeviceListVO.getSubProductId(), subDeviceListVO.getSubDeviceNo());
|
||||
for (ThingsModelDTO thingsModelDTO : subList) {
|
||||
thingsModelDTO.setProductId(subDeviceListVO.getSubProductId());
|
||||
thingsModelDTO.setSerialNumber(subDeviceListVO.getSubDeviceNo());
|
||||
@ -1863,21 +1945,22 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
list.addAll(subList);
|
||||
}
|
||||
//指令控制
|
||||
// 指令控制
|
||||
jurisdictionThingsModel(list, deviceId);
|
||||
return list;
|
||||
}
|
||||
|
||||
private void jurisdictionThingsModel(List<ThingsModelDTO> list, Long deviceId) {
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
//查询是否有指令控制
|
||||
// 查询是否有指令控制
|
||||
List<OrderControl> orderControls = orderControlService.selectByUserId(userId, deviceId);
|
||||
if (CollectionUtils.isEmpty(orderControls)) return;
|
||||
if (CollectionUtils.isEmpty(orderControls))
|
||||
return;
|
||||
List<Long> modelIdList = new ArrayList<>();
|
||||
for (OrderControl control : orderControls) {
|
||||
String selectOrder = control.getSelectOrder();
|
||||
String[] split = selectOrder.split(",");
|
||||
//判断是否生效
|
||||
// 判断是否生效
|
||||
boolean just = just(control);
|
||||
if (just) {
|
||||
List<Long> ids = Arrays.stream(split).map(Long::valueOf).collect(Collectors.toList());
|
||||
@ -1906,11 +1989,12 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
|
||||
@Override
|
||||
public void updateByOrder(Long userId, Long deviceId) {
|
||||
//处理指令权限问题 ,非管理员角色
|
||||
// 处理指令权限问题 ,非管理员角色
|
||||
List<OrderControl> controlList = orderControlService.selectByUserId(userId, deviceId);
|
||||
controlList = controlList.stream().filter(control -> Objects.equals(control.getStatus(), "1")).collect(Collectors.toList());
|
||||
controlList = controlList.stream().filter(control -> Objects.equals(control.getStatus(), "1"))
|
||||
.collect(Collectors.toList());
|
||||
if (!CollectionUtils.isEmpty(controlList)) {
|
||||
//更新次数
|
||||
// 更新次数
|
||||
OrderControl control = controlList.get(0);
|
||||
control.setCount(control.getCount() - 1);
|
||||
orderControlService.updateOrderControl(control);
|
||||
@ -1928,7 +2012,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
return deviceMapper.listDeviceGroupByGroupIds(groupIds);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询产品下所有设备,返回设备编号
|
||||
*
|
||||
@ -1940,7 +2023,4 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
return deviceMapper.selectSerialNumbersByProductId(productId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -125,6 +125,7 @@ public class ModbusConfigServiceImpl implements IModbusConfigService {
|
||||
this.updateModbusConfig(config);
|
||||
}
|
||||
}
|
||||
// 将寄存器配置保存到redis缓存
|
||||
modbusConfigCache.setModbusConfigCacheByProductId(productId);
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,6 @@ import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.SecurityUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.iot.cache.ICalculatedThingsModelCache;
|
||||
import com.fastbee.iot.cache.ITSLCache;
|
||||
import com.fastbee.iot.domain.*;
|
||||
import com.fastbee.iot.mapper.*;
|
||||
@ -68,8 +67,6 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
private ITSLValueCache thingModelCache;
|
||||
@Resource
|
||||
private ITSLCache itslCache;
|
||||
@Resource
|
||||
private ICalculatedThingsModelCache calculatedThingsModelCache;
|
||||
|
||||
private static List<SceneModelData> getSceneModelData(SceneModelDevice sceneModelDevice, List<ThingsModelDTO> thingsModelDTOList) {
|
||||
List<SceneModelData> modelDataList = new ArrayList<>();
|
||||
@ -142,11 +139,16 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
*/
|
||||
@Override
|
||||
public int insertThingsModel(ThingsModel thingsModel) {
|
||||
// 去除计算公式中的空格
|
||||
if (StringUtils.isNotEmpty(thingsModel.getFormula())) {
|
||||
thingsModel.setFormula(thingsModel.getFormula().replaceAll("\\s+", ""));
|
||||
}
|
||||
ThingsModel input = new ThingsModel();
|
||||
input.setProductId(thingsModel.getProductId());
|
||||
input.setIsCalculated(0); // 只需要查产品的物模型即可
|
||||
input.setLanguage(SecurityUtils.getLanguage());
|
||||
List<ThingsModel> list = thingsModelMapper.selectThingsModelList(input);
|
||||
Boolean isRepeat = list.stream().anyMatch(x -> x.getIdentifier().equals(thingsModel.getIdentifier()));
|
||||
boolean isRepeat = list.stream().anyMatch(x -> x.getIdentifier().equals(thingsModel.getIdentifier()));
|
||||
if (!isRepeat) {
|
||||
SysUser user = getLoginUser().getUser();
|
||||
if (null != user.getDeptId()) {
|
||||
@ -157,21 +159,19 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
thingsModel.setTenantName(user.getUserName());
|
||||
}
|
||||
thingsModel.setCreateTime(DateUtils.getNowDate());
|
||||
// 自动判断是否为纯计算物模型:如果公式包含{},则设置为1(纯计算)
|
||||
if (thingsModel.getIsCalculated() == null) {
|
||||
thingsModel.setIsCalculated(determineIsCalculated(thingsModel.getFormula()));
|
||||
}
|
||||
// 插入物模型数据库
|
||||
int result = thingsModelMapper.insertThingsModel(thingsModel);
|
||||
// 更新redis缓存
|
||||
// 更新redis缓存:IsCalculated=1为纯计算物模型
|
||||
if (thingsModel.getIsCalculated() != null && thingsModel.getIsCalculated() == 1) {
|
||||
itslCache.refreshDeviceCalculatedModelCache(thingsModel.getSerialNumber());
|
||||
} else {
|
||||
itslCache.setCacheThingsModelByProductId(thingsModel.getProductId());
|
||||
// 刷新纯计算物模型缓存
|
||||
if (result > 0) {
|
||||
calculatedThingsModelCache.refreshCalculatedCache(thingsModel.getProductId());
|
||||
}
|
||||
|
||||
if (result > 0) {
|
||||
// 更新场景管理变量
|
||||
List<SceneModelDevice> sceneModelDeviceList = sceneModelDeviceMapper.listDeviceByProductId(thingsModel.getProductId());
|
||||
if (org.apache.commons.collections4.CollectionUtils.isEmpty(sceneModelDeviceList)) {
|
||||
if (CollectionUtils.isEmpty(sceneModelDeviceList)) {
|
||||
return result;
|
||||
}
|
||||
List<ThingsModelDTO> thingsModelDTOList = this.changeObjectOrArray(thingsModel);
|
||||
@ -219,15 +219,15 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
thingsModel.setIsChart(template.getIsChart());
|
||||
thingsModel.setIsHistory(template.getIsHistory());
|
||||
thingsModel.setModelOrder(template.getModelOrder());
|
||||
thingsModel.setIsCalculated(determineIsCalculated(thingsModel.getFormula()));
|
||||
Boolean isRepeat = dbList.stream().anyMatch(x -> x.getIdentifier().equals(thingsModel.getIdentifier()));
|
||||
thingsModel.setIsCalculated(0);
|
||||
boolean isRepeat = dbList.stream().anyMatch(x -> x.getIdentifier().equals(thingsModel.getIdentifier()));
|
||||
if (isRepeat) {
|
||||
repeatCount = repeatCount + 1;
|
||||
} else {
|
||||
list.add(thingsModel);
|
||||
}
|
||||
}
|
||||
if (list.size() > 0) {
|
||||
if (!list.isEmpty()) {
|
||||
int result = thingsModelMapper.insertBatchThingsModel(list);
|
||||
if (result > 0) {
|
||||
// 更新场景管理变量
|
||||
@ -245,8 +245,6 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
}
|
||||
//更新redis缓存
|
||||
itslCache.setCacheThingsModelByProductId(input.getProductId());
|
||||
// 刷新纯计算物模型缓存
|
||||
calculatedThingsModelCache.refreshCalculatedCache(input.getProductId());
|
||||
}
|
||||
return repeatCount;
|
||||
}
|
||||
@ -271,16 +269,12 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
Boolean isRepeat = list.stream().anyMatch(x -> x.getIdentifier().equals(thingsModel.getIdentifier()) && x.getModelId().longValue() != thingsModel.getModelId());
|
||||
if (!isRepeat) {
|
||||
thingsModel.setUpdateTime(DateUtils.getNowDate());
|
||||
// 自动判断是否为纯计算物模型:如果公式包含{},则设置为1(纯计算)
|
||||
if (thingsModel.getIsCalculated() == null) {
|
||||
thingsModel.setIsCalculated(determineIsCalculated(thingsModel.getFormula()));
|
||||
}
|
||||
int result = thingsModelMapper.updateThingsModel(thingsModel);
|
||||
// 更新redis缓存
|
||||
if (thingsModel.getIsCalculated() != null && thingsModel.getIsCalculated() == 1) {
|
||||
itslCache.refreshDeviceCalculatedModelCache(thingsModel.getSerialNumber());
|
||||
} else {
|
||||
itslCache.setCacheThingsModelByProductId(thingsModel.getProductId());
|
||||
// 刷新纯计算物模型缓存
|
||||
if (result > 0) {
|
||||
calculatedThingsModelCache.refreshCalculatedCache(thingsModel.getProductId());
|
||||
}
|
||||
if (result > 0) {
|
||||
List<SceneModelData> sceneModelDataList = sceneModelDataMapper.selectSceneDeviceThingsModelList(thingsModel.getModelId());
|
||||
@ -320,12 +314,9 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
if (result > 0) {
|
||||
sceneModelDataMapper.deleteThingsModelByDatasourceId(modelIds);
|
||||
}
|
||||
// 更新redis缓存
|
||||
// 更新redis缓存 产品和设备物模型都干掉
|
||||
itslCache.setCacheThingsModelByProductId(thingsModel.getProductId());
|
||||
// 刷新纯计算物模型缓存
|
||||
if (result > 0 && thingsModel != null && thingsModel.getProductId() != null) {
|
||||
calculatedThingsModelCache.refreshCalculatedCache(thingsModel.getProductId());
|
||||
}
|
||||
itslCache.refreshDeviceCalculatedModelCache(thingsModel.getSerialNumber());
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -341,12 +332,11 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
ThingsModel thingsModel = thingsModelMapper.selectThingsModelByModelId(modelId, SecurityUtils.getLanguage());
|
||||
int result = thingsModelMapper.deleteThingsModelByModelId(modelId);
|
||||
// 更新redis缓存
|
||||
if (thingsModel != null && thingsModel.getProductId() != null) {
|
||||
if (thingsModel != null) {
|
||||
if (thingsModel.getProductId() != null) {
|
||||
itslCache.setCacheThingsModelByProductId(thingsModel.getProductId());
|
||||
// 刷新纯计算物模型缓存
|
||||
if (result > 0) {
|
||||
calculatedThingsModelCache.refreshCalculatedCache(thingsModel.getProductId());
|
||||
}
|
||||
itslCache.refreshDeviceCalculatedModelCache(thingsModel.getSerialNumber());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -354,8 +344,8 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
/**
|
||||
* 获取单个物模型获取
|
||||
*
|
||||
* @param productId
|
||||
* @param identify
|
||||
* @param productId 产品ID
|
||||
* @param identify 物模型标识符
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@ -695,6 +685,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
if (null == productId) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 1、获取产品物模型
|
||||
Map<String, ThingsModelValueItem> map = itslCache.getCacheThMapByProductId(productId);
|
||||
List<ThingsModelDTO> thingsModelDTOList = new ArrayList<>();
|
||||
for (Map.Entry<String, ThingsModelValueItem> entry : map.entrySet()) {
|
||||
@ -702,6 +693,15 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
thingsModelDTOList.addAll(this.changeObjectOrArray(thingsModel));
|
||||
}
|
||||
|
||||
// 2. 获取设备级计算物模型并合并
|
||||
if (StringUtils.isNotEmpty(serialNumber)) {
|
||||
Map<String, ThingsModelValueItem> deviceCalculatedMap = itslCache.getDeviceCalculatedModelMap(serialNumber);
|
||||
for (Map.Entry<String, ThingsModelValueItem> entry : deviceCalculatedMap.entrySet()) {
|
||||
ThingsModel thingsModel = this.propertyDtoChangeThingsModel(entry.getValue());
|
||||
thingsModelDTOList.addAll(this.changeObjectOrArray(thingsModel));
|
||||
}
|
||||
}
|
||||
|
||||
thingsModelDTOList.sort(Comparator.comparing(ThingsModelDTO::getModelOrder));
|
||||
if (StringUtils.isEmpty(serialNumber)) {
|
||||
return thingsModelDTOList;
|
||||
|
||||
@ -163,15 +163,71 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectDeviceVo">
|
||||
select device_id, device_name, product_id, product_name, tenant_id, tenant_name, serial_number,gw_dev_code, firmware_version, status, rssi,is_shadow ,is_simulate,location_way,things_model_value,network_address, network_ip, longitude, latitude, active_time, create_time, update_time, img_url,summary,remark,slave_id from iot_device
|
||||
select device_id,
|
||||
device_name,
|
||||
product_id,
|
||||
product_name,
|
||||
tenant_id,
|
||||
tenant_name,
|
||||
serial_number,
|
||||
gw_dev_code,
|
||||
firmware_version,
|
||||
status,
|
||||
rssi,
|
||||
is_shadow,
|
||||
is_simulate,
|
||||
location_way,
|
||||
things_model_value,
|
||||
network_address,
|
||||
network_ip,
|
||||
longitude,
|
||||
latitude,
|
||||
active_time,
|
||||
create_time,
|
||||
update_time,
|
||||
img_url,
|
||||
summary,
|
||||
remark,
|
||||
slave_id
|
||||
from iot_device
|
||||
</sql>
|
||||
|
||||
<sql id="selectDeviceShortVo">
|
||||
select device_id, device_name, product_id, product_name, tenant_id, tenant_name, serial_number, firmware_version, status,rssi,is_shadow ,is_simulate,location_way,things_model_value, active_time,img_url,slave_id from iot_device
|
||||
select device_id,
|
||||
device_name,
|
||||
product_id,
|
||||
product_name,
|
||||
tenant_id,
|
||||
tenant_name,
|
||||
serial_number,
|
||||
firmware_version,
|
||||
status,
|
||||
rssi,
|
||||
is_shadow,
|
||||
is_simulate,
|
||||
location_way,
|
||||
things_model_value,
|
||||
active_time,
|
||||
img_url,
|
||||
slave_id
|
||||
from iot_device
|
||||
</sql>
|
||||
|
||||
<sql id="selectWebhookDeviceVo">
|
||||
select device_id, device_name,product_id, serial_number,tenant_id, tenant_name, status,is_shadow,is_simulate, rssi ,location_way,things_model_value, active_time from iot_device
|
||||
select device_id,
|
||||
device_name,
|
||||
product_id,
|
||||
serial_number,
|
||||
tenant_id,
|
||||
tenant_name,
|
||||
status,
|
||||
is_shadow,
|
||||
is_simulate,
|
||||
rssi,
|
||||
location_way,
|
||||
things_model_value,
|
||||
active_time
|
||||
from iot_device
|
||||
</sql>
|
||||
|
||||
<select id="selectDeviceList" parameterType="com.fastbee.iot.domain.Device" resultMap="DeviceResult">
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
<result property="modelName_en_US" column="model_name_en_us"/>
|
||||
<result property="productId" column="product_id"/>
|
||||
<result property="productName" column="product_name"/>
|
||||
<result property="deviceId" column="device_id"/>
|
||||
<result property="serialNumber" column="serial_number"/>
|
||||
<result property="tenantId" column="tenant_id"/>
|
||||
<result property="tenantName" column="tenant_name"/>
|
||||
<result property="identifier" column="identifier"/>
|
||||
@ -52,6 +54,8 @@
|
||||
model_name,
|
||||
product_id,
|
||||
product_name,
|
||||
device_id,
|
||||
serial_number,
|
||||
tenant_id,
|
||||
tenant_name,
|
||||
identifier,
|
||||
@ -79,72 +83,134 @@
|
||||
<select id="selectThingsModelList" parameterType="com.fastbee.iot.domain.ThingsModel" resultMap="ThingsModelResult">
|
||||
select m.model_id,
|
||||
case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as model_name,
|
||||
m.model_name as model_name_zh_cn, t.en_us as model_name_en_us,
|
||||
m.product_id, m.product_name, m.tenant_id, m.tenant_name, m.identifier, m.type, m.datatype, m.formula,
|
||||
m.is_calculated, m.specs, m.is_chart, m.is_share_perm, m.is_history, m.is_monitor, m.is_app, m.del_flag, m.create_by,
|
||||
m.create_time, m.update_by, m.update_time, m.remark, m.is_readonly, m.model_order
|
||||
from iot_things_model m
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' THEN t.en_us ELSE m.model_name END AS model_name,
|
||||
m.model_name AS model_name_zh_cn,
|
||||
t.en_us AS model_name_en_us,
|
||||
m.product_id,
|
||||
m.product_name,
|
||||
m.device_id,
|
||||
m.serial_number,
|
||||
m.tenant_id,
|
||||
m.tenant_name,
|
||||
m.identifier,
|
||||
m.type,
|
||||
m.datatype,
|
||||
m.formula,
|
||||
m.is_calculated,
|
||||
m.specs,
|
||||
m.is_chart,
|
||||
m.is_share_perm,
|
||||
m.is_history,
|
||||
m.is_monitor,
|
||||
m.is_app,
|
||||
m.del_flag,
|
||||
m.create_by,
|
||||
m.create_time,
|
||||
m.update_by,
|
||||
m.update_time,
|
||||
m.remark,
|
||||
m.is_readonly,
|
||||
m.model_order
|
||||
FROM
|
||||
iot_things_model m
|
||||
LEFT JOIN iot_things_model_translate t ON m.model_id = t.id
|
||||
<where>
|
||||
<if test="productId != null">
|
||||
and m.product_id = #{productId}
|
||||
AND m.product_id = #{productId}
|
||||
</if>
|
||||
<if test="serialNumber != null">
|
||||
AND m.serial_number = #{serialNumber}
|
||||
</if>
|
||||
<if test="deviceId != null">
|
||||
AND m.device_id = #{deviceId}
|
||||
</if>
|
||||
<if test="type!=null">
|
||||
and type = #{type}
|
||||
AND type = #{type}
|
||||
</if>
|
||||
<if test="isChart != null ">
|
||||
and is_chart = #{isChart}
|
||||
AND is_chart = #{isChart}
|
||||
</if>
|
||||
<if test="isHistory != null ">
|
||||
and is_history = #{isHistory}
|
||||
AND is_history = #{isHistory}
|
||||
</if>
|
||||
<if test="isMonitor != null ">
|
||||
and is_monitor = #{isMonitor}
|
||||
AND is_monitor = #{isMonitor}
|
||||
</if>
|
||||
<if test="isReadonly != null ">
|
||||
and is_readonly = #{isReadonly}
|
||||
AND is_readonly = #{isReadonly}
|
||||
</if>
|
||||
<if test="isCalculated != null ">
|
||||
AND is_calculated = #{isCalculated}
|
||||
</if>
|
||||
<if test="modelIdList != null ">
|
||||
model_id in
|
||||
model_id IN
|
||||
<foreach collection="modelIdList" item="item" separator="," open="(" close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
order by model_order desc,create_time desc
|
||||
ORDER BY
|
||||
model_order DESC,
|
||||
create_time DESC
|
||||
</select>
|
||||
|
||||
<select id="selectThingsModelPermList" resultMap="ThingsModelPermResult">
|
||||
select case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as model_name,
|
||||
m.identifier, m.is_share_perm, m.remark
|
||||
from iot_things_model m
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
where m.product_id=#{productId} and is_share_perm=1
|
||||
order by model_order desc,create_time desc
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' THEN t.en_us ELSE m.model_name END AS model_name,
|
||||
m.identifier,
|
||||
m.is_share_perm,
|
||||
m.remark
|
||||
FROM
|
||||
iot_things_model m
|
||||
LEFT JOIN iot_things_model_translate t ON m.model_id = t.id
|
||||
WHERE
|
||||
m.product_id = #{productId}
|
||||
AND is_share_perm = 1
|
||||
ORDER BY
|
||||
model_order DESC,
|
||||
create_time DESC
|
||||
</select>
|
||||
|
||||
|
||||
<select id="selectThingsModelListByProductIds" parameterType="String" resultMap="ThingsModelResult">
|
||||
select m.model_id,
|
||||
case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as model_name,
|
||||
m.product_id, m.product_name, m.tenant_id, m.tenant_name, m.identifier, m.type, m.datatype, m.formula,
|
||||
m.is_calculated, m.specs, m.is_chart, m.is_share_perm, m.is_history, m.is_monitor, m.is_app, m.del_flag, m.create_by,
|
||||
m.create_time, m.update_by, m.update_time, m.remark, m.is_readonly, m.model_order
|
||||
from iot_things_model m
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
where is_chart=1 and m.product_id in
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' THEN t.en_us ELSE m.model_name END AS model_name,
|
||||
m.product_id,
|
||||
m.product_name,
|
||||
m.tenant_id,
|
||||
m.tenant_name,
|
||||
m.identifier,
|
||||
m.type,
|
||||
m.datatype,
|
||||
m.formula,
|
||||
m.is_calculated,
|
||||
m.specs,
|
||||
m.is_chart,
|
||||
m.is_share_perm,
|
||||
m.is_history,
|
||||
m.is_monitor,
|
||||
m.is_app,
|
||||
m.del_flag,
|
||||
m.create_by,
|
||||
m.create_time,
|
||||
m.update_by,
|
||||
m.update_time,
|
||||
m.remark,
|
||||
m.is_readonly,
|
||||
m.model_order
|
||||
FROM
|
||||
iot_things_model m
|
||||
LEFT JOIN iot_things_model_translate t ON m.model_id = t.id
|
||||
WHERE
|
||||
is_chart = 1
|
||||
AND m.product_id IN
|
||||
<foreach item="productId" collection="array" open="(" separator="," close=")">
|
||||
#{productId}
|
||||
</foreach>
|
||||
@ -153,43 +219,85 @@
|
||||
<select id="selectThingsModelByModelId" resultMap="ThingsModelResult">
|
||||
select m.model_id,
|
||||
case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as model_name,
|
||||
m.product_id, m.product_name, m.tenant_id, m.tenant_name, m.identifier, m.type, m.datatype, m.formula,
|
||||
m.specs, m.is_chart, m.is_share_perm, m.is_history, m.is_monitor, m.is_app, m.del_flag, m.create_by,
|
||||
m.create_time, m.update_by, m.update_time, m.remark, m.is_readonly, m.model_order
|
||||
from iot_things_model m
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
where model_id = #{modelId}
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' THEN t.en_us ELSE m.model_name END AS model_name,
|
||||
m.product_id,
|
||||
m.product_name,
|
||||
m.serial_number,
|
||||
m.tenant_id,
|
||||
m.tenant_name,
|
||||
m.identifier,
|
||||
m.type,
|
||||
m.datatype,
|
||||
m.formula,
|
||||
m.specs,
|
||||
m.is_chart,
|
||||
m.is_share_perm,
|
||||
m.is_history,
|
||||
m.is_monitor,
|
||||
m.is_app,
|
||||
m.del_flag,
|
||||
m.create_by,
|
||||
m.create_time,
|
||||
m.update_by,
|
||||
m.update_time,
|
||||
m.remark,
|
||||
m.is_readonly,
|
||||
m.model_order
|
||||
FROM
|
||||
iot_things_model m
|
||||
LEFT JOIN iot_things_model_translate t ON m.model_id = t.id
|
||||
WHERE
|
||||
model_id = #{modelId}
|
||||
</select>
|
||||
|
||||
<select id="selectSingleThingsModel" parameterType="com.fastbee.iot.domain.ThingsModel" resultMap="ThingsModelResult">
|
||||
select m.model_id,
|
||||
case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as model_name,
|
||||
m.product_id, m.product_name, m.tenant_id, m.tenant_name, m.identifier, m.type, m.datatype, m.formula,
|
||||
m.is_calculated, m.specs, m.is_chart, m.is_share_perm, m.is_history, m.is_monitor, m.is_app, m.del_flag, m.create_by,
|
||||
m.create_time, m.update_by, m.update_time, m.remark, m.is_readonly, m.model_order
|
||||
from iot_things_model m
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' THEN t.en_us ELSE m.model_name END AS model_name,
|
||||
m.product_id,
|
||||
m.product_name,
|
||||
m.tenant_id,
|
||||
m.tenant_name,
|
||||
m.identifier,
|
||||
m.type,
|
||||
m.datatype,
|
||||
m.formula,
|
||||
m.is_calculated,
|
||||
m.specs,
|
||||
m.is_chart,
|
||||
m.is_share_perm,
|
||||
m.is_history,
|
||||
m.is_monitor,
|
||||
m.is_app,
|
||||
m.del_flag,
|
||||
m.create_by,
|
||||
m.create_time,
|
||||
m.update_by,
|
||||
m.update_time,
|
||||
m.remark,
|
||||
m.is_readonly,
|
||||
m.model_order
|
||||
FROM
|
||||
iot_things_model m
|
||||
LEFT JOIN iot_things_model_translate t ON m.model_id = t.id
|
||||
<where>
|
||||
<if test="productId != null">
|
||||
and m.product_id = #{productId}
|
||||
AND m.product_id = #{productId}
|
||||
</if>
|
||||
<if test="identifier !=null and identifier != ''">
|
||||
and identifier = #{identifier}
|
||||
AND identifier = #{identifier}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<insert id="insertThingsModel" parameterType="com.fastbee.iot.domain.ThingsModel" useGeneratedKeys="true"
|
||||
keyProperty="modelId">
|
||||
insert into iot_things_model
|
||||
INSERT INTO
|
||||
iot_things_model
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="modelName != null and modelName != ''">
|
||||
model_name,
|
||||
@ -200,6 +308,12 @@
|
||||
<if test="productName != null and productName != ''">
|
||||
product_name,
|
||||
</if>
|
||||
<if test="deviceId != null">
|
||||
device_id,
|
||||
</if>
|
||||
<if test="serialNumber != null and serialNumber != ''">
|
||||
serial_number,
|
||||
</if>
|
||||
<if test="tenantId != null">
|
||||
tenant_id,
|
||||
</if>
|
||||
@ -274,6 +388,12 @@
|
||||
<if test="productName != null and productName != ''">
|
||||
#{productName},
|
||||
</if>
|
||||
<if test="deviceId != null">
|
||||
#{deviceId},
|
||||
</if>
|
||||
<if test="serialNumber != null and serialNumber != ''">
|
||||
#{serialNumber},
|
||||
</if>
|
||||
<if test="tenantId != null">
|
||||
#{tenantId},
|
||||
</if>
|
||||
@ -342,7 +462,8 @@
|
||||
|
||||
<insert id="insertBatchThingsModel" parameterType="com.fastbee.iot.domain.ThingsModel" useGeneratedKeys="true"
|
||||
keyProperty="modelId">
|
||||
INSERT INTO iot_things_model (
|
||||
INSERT INTO
|
||||
iot_things_model (
|
||||
model_name,
|
||||
product_id,
|
||||
product_name,
|
||||
@ -365,18 +486,31 @@
|
||||
)
|
||||
VALUES
|
||||
<foreach item="model" collection="list" separator=",">
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
#{model.modelName},#{model.productId},#{model.productName},#{model.tenantId},
|
||||
#{model.tenantName},#{model.identifier},#{model.type},#{model.datatype},
|
||||
#{model.specs},#{model.isChart},#{model.isSharePerm},#{model.isHistory},
|
||||
#{model.isMonitor},#{model.isApp},#{model.isReadonly},#{model.createBy},#{model.createTime},
|
||||
#{model.formula},#{model.isCalculated}
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">#{model.modelName},
|
||||
#{model.productId},
|
||||
#{model.productName},
|
||||
#{model.tenantId},
|
||||
#{model.tenantName},
|
||||
#{model.identifier},
|
||||
#{model.type},
|
||||
#{model.datatype},
|
||||
#{model.specs},
|
||||
#{model.isChart},
|
||||
#{model.isSharePerm},
|
||||
#{model.isHistory},
|
||||
#{model.isMonitor},
|
||||
#{model.isApp},
|
||||
#{model.isReadonly},
|
||||
#{model.createBy},
|
||||
#{model.createTime},
|
||||
#{model.formula},
|
||||
#{model.isCalculated}
|
||||
</trim>
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<update id="updateThingsModel" parameterType="com.fastbee.iot.domain.ThingsModel">
|
||||
update iot_things_model
|
||||
UPDATE iot_things_model
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="modelName != null and modelName != ''">
|
||||
model_name = #{modelName},
|
||||
@ -387,6 +521,12 @@
|
||||
<if test="productName != null and productName != ''">
|
||||
product_name = #{productName},
|
||||
</if>
|
||||
<if test="deviceId != null">
|
||||
device_id = #{deviceId},
|
||||
</if>
|
||||
<if test="serialNumber != null and serialNumber != ''">
|
||||
serial_number = #{serialNumber},
|
||||
</if>
|
||||
<if test="tenantId != null">
|
||||
tenant_id = #{tenantId},
|
||||
</if>
|
||||
@ -451,17 +591,20 @@
|
||||
model_order = #{modelOrder},
|
||||
</if>
|
||||
</trim>
|
||||
where model_id = #{modelId}
|
||||
WHERE
|
||||
model_id = #{modelId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteThingsModelByModelId" parameterType="Long">
|
||||
delete
|
||||
from iot_things_model
|
||||
where model_id = #{modelId}
|
||||
DELETE FROM iot_things_model
|
||||
WHERE
|
||||
model_id = #{modelId}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteThingsModelByModelIds" parameterType="String">
|
||||
delete from iot_things_model where model_id in
|
||||
DELETE FROM iot_things_model
|
||||
WHERE
|
||||
model_id IN
|
||||
<foreach item="modelId" collection="array" open="(" separator="," close=")">
|
||||
#{modelId}
|
||||
</foreach>
|
||||
@ -475,16 +618,17 @@
|
||||
m.is_history as isHistory,
|
||||
m.specs as dataType,
|
||||
case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as name,
|
||||
m.type as type,
|
||||
m.formula as formula,
|
||||
from iot_things_model m
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
where m.product_id = #{productId,jdbcType=BIGINT}
|
||||
and m.identifier in
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' THEN t.en_us ELSE m.model_name END AS name,
|
||||
m.type AS type,
|
||||
m.formula AS formula,
|
||||
FROM
|
||||
iot_things_model m
|
||||
LEFT JOIN iot_things_model_translate t ON m.model_id = t.id
|
||||
WHERE
|
||||
m.product_id = #{productId,jdbcType=BIGINT}
|
||||
AND m.identifier IN
|
||||
<foreach collection="ids" item="item" open="(" separator="," close=")">
|
||||
#{item}
|
||||
</foreach>
|
||||
@ -492,43 +636,50 @@
|
||||
|
||||
|
||||
<delete id="deleteThingsModelByProductId" parameterType="long">
|
||||
delete from iot_things_model where product_id = #{productId}
|
||||
DELETE FROM iot_things_model
|
||||
WHERE
|
||||
product_id = #{productId}
|
||||
</delete>
|
||||
|
||||
<select id="listSimByProductIds" resultType="com.fastbee.iot.model.ThingsModelSimVO">
|
||||
select m.product_id, m.identifier,
|
||||
case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as model_name,
|
||||
m.type as thingsModelType,
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' THEN t.en_us ELSE m.model_name END AS model_name,
|
||||
m.type AS thingsModelType,
|
||||
m.datatype
|
||||
from iot_things_model m
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
where m.product_id in
|
||||
FROM
|
||||
iot_things_model m
|
||||
LEFT JOIN iot_things_model_translate t ON m.model_id = t.id
|
||||
WHERE
|
||||
m.product_id IN
|
||||
<foreach collection="productIdList" item="productId" open="(" separator="," close=")">
|
||||
#{productId}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="getSpecsByProductIdAndIdentifier" resultType="java.lang.String">
|
||||
select specs
|
||||
from iot_things_model
|
||||
where product_id = #{productId}
|
||||
and identifier = #{identifier}
|
||||
SELECT
|
||||
specs
|
||||
FROM
|
||||
iot_things_model
|
||||
WHERE
|
||||
product_id = #{productId}
|
||||
AND identifier = #{identifier}
|
||||
</select>
|
||||
|
||||
<select id="listSceneModelDataByModelIds" resultType="com.fastbee.iot.domain.ThingsModel">
|
||||
select m.model_id, m.identifier,
|
||||
case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as model_name,
|
||||
from iot_things_model m
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
where m.model_id in
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' THEN t.en_us ELSE m.model_name END AS model_name,
|
||||
FROM
|
||||
iot_things_model m
|
||||
LEFT JOIN iot_things_model_translate t ON m.model_id = t.id
|
||||
WHERE
|
||||
m.model_id IN
|
||||
<foreach collection="modelIdList" item="modelId" open="(" separator="," close=")">
|
||||
#{modelId}
|
||||
</foreach>
|
||||
@ -538,40 +689,50 @@
|
||||
<select id="getModbusConfigUnSelectThingsModel" resultMap="ThingsModelAndModbusResult">
|
||||
select m.model_id, m.identifier,
|
||||
case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as model_name,
|
||||
m.datatype,
|
||||
case
|
||||
when (select count(1) from iot_modbus_config c where c.product_id = #{productId} and c.identifier = m.identifier) > 0 then 0
|
||||
when (select count(1) from iot_modbus_config c where c.product_id = #{productId}
|
||||
and c.identifier = m.identifier) > 0 then 0
|
||||
else 1
|
||||
end as is_select
|
||||
from iot_things_model m
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
where m.product_id = #{productId}
|
||||
AND m.del_flag = 0
|
||||
AND m.is_calculated = 0
|
||||
</select>
|
||||
|
||||
<select id="selectThingsModelListByModelIds" resultType="com.fastbee.iot.domain.ThingsModel">
|
||||
select model_id, type
|
||||
from iot_things_model where model_id in
|
||||
SELECT
|
||||
model_id,
|
||||
type
|
||||
FROM
|
||||
iot_things_model
|
||||
WHERE
|
||||
model_id IN
|
||||
<foreach item="modelId" collection="modelIdList" open="(" separator="," close=")">
|
||||
#{modelId}
|
||||
</foreach>
|
||||
and del_flag = 0
|
||||
AND del_flag = 0
|
||||
</select>
|
||||
|
||||
<select id="selectThingsModelBySerialNumber" resultMap="ThingsModelResult">
|
||||
select m.model_id, m.identifier,
|
||||
case
|
||||
when #{language} = 'zh-CN' then m.model_name
|
||||
when #{language} = 'en-US' then t.en_us
|
||||
else m.model_name
|
||||
end as model_name
|
||||
from iot_things_model m
|
||||
inner join iot_device d on m.product_id = d.product_id
|
||||
left join iot_things_model_translate t on m.model_id = t.id
|
||||
where d.device_id = #{deviceId}
|
||||
and m.is_readonly = 0
|
||||
when #{language}
|
||||
= 'zh-CN' THEN m.model_name WHEN #{language}
|
||||
= 'en-US' THEN t.en_us ELSE m.model_name END AS model_name
|
||||
FROM
|
||||
iot_things_model m
|
||||
INNER JOIN iot_device d ON m.product_id = d.product_id
|
||||
LEFT JOIN iot_things_model_translate t ON m.model_id = t.id
|
||||
WHERE
|
||||
d.device_id = #{deviceId}
|
||||
AND m.is_readonly = 0
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@ -33,3 +33,14 @@ WHERE `formula` IS NOT NULL
|
||||
AND `formula` != ''
|
||||
AND `formula` LIKE '%{%}%'
|
||||
AND `del_flag` = '0';
|
||||
|
||||
-- 1. 添加 device_id 字段(默认为0,表示产品级物模型)
|
||||
ALTER TABLE `iot_things_model`
|
||||
ADD COLUMN `device_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '设备ID,0表示产品级物模型,>0表示设备级计算物模型' AFTER `product_name`;
|
||||
|
||||
-- 2. 添加 serial_number 字段
|
||||
ALTER TABLE `iot_things_model`
|
||||
ADD COLUMN `serial_number` varchar(64) NULL DEFAULT NULL COMMENT '设备编号' AFTER `device_id`;
|
||||
|
||||
-- 3. 添加索引(设备编号索引)
|
||||
ALTER TABLE `iot_things_model` ADD INDEX `idx_calculated_serial` (`is_calculated`, `serial_number`);
|
||||
|
||||
@ -109,6 +109,7 @@
|
||||
"device.device-edit.148398-80": "Alert user",
|
||||
"device.device-edit.148398-81": "equipment alarm ",
|
||||
"device.device-edit.148398-82": "Voice intercom",
|
||||
"device.device-edit.148398-83": "Calculation Formula",
|
||||
"device.device-functionlog.399522-0": "Please Select A Device Slave:",
|
||||
"device.device-functionlog.399522-1": "Please Select A Device Slave",
|
||||
"device.device-functionlog.399522-2": "Slave Address:$",
|
||||
|
||||
@ -298,7 +298,6 @@
|
||||
"product.product-things-model.142341-52": "Please enter the step size, for example: 1",
|
||||
"product.product-things-model.142341-53": "The upstream data of the device is displayed after being calculated using a formula.",
|
||||
"product.product-things-model.142341-54": "%s in the formula is a placeholder representing the current thing model value.",
|
||||
"product.product-things-model.142341-54-1": "Use {identifier} to reference values from other thing models, supporting multiple thing models in calculations.",
|
||||
"product.product-things-model.142341-54-2": "Supported operators: +, -, *, /, % (modulo), ^ (power), parentheses ().",
|
||||
"product.product-things-model.142341-55": "For example:",
|
||||
"product.product-things-model.142341-56": "Add: %s+10",
|
||||
@ -307,10 +306,6 @@
|
||||
"product.product-things-model.142341-59": "Divide: %s/10",
|
||||
"product.product-things-model.142341-60": "Divide (retain decimals): %s%10.00",
|
||||
"product.product-things-model.142341-61": "Retain decimals",
|
||||
"product.product-things-model.142341-61-1": "Examples of referencing other thing models:",
|
||||
"product.product-things-model.142341-61-2": "Pressure = K * frequency^2 - constant: 1.5*{frequency}^2-100",
|
||||
"product.product-things-model.142341-61-3": "Speed = distance / time: {distance}/{time}",
|
||||
"product.product-things-model.142341-61-4": "Mixed calculation: %s*{factor}+{offset}",
|
||||
"product.product-things-model.142341-62": "Control Formula",
|
||||
"product.product-things-model.142341-63": "Boolean value",
|
||||
"product.product-things-model.142341-64": "For example: Close",
|
||||
@ -551,5 +546,9 @@
|
||||
"product.product-modbus-task.894593-67": "Tips",
|
||||
"product.product-modbus-task.894593-68": "determine",
|
||||
"product.product-modbus-task.894593-69": "cancel",
|
||||
"product.product-modbus-task.894593-70": "Cancelled"
|
||||
"product.product-modbus-task.894593-70": "Cancelled",
|
||||
"product.product-things-model.142341-200": "Click symbols to build the formula",
|
||||
"product.product-things-model.142341-201": "Reference product model identifiers",
|
||||
"product.product-things-model.142341-202": "No available identifiers",
|
||||
"product.product-things-model.142341-203": "Add Calculated Model"
|
||||
}
|
||||
|
||||
@ -109,6 +109,7 @@
|
||||
"device.device-edit.148398-80": "告警用户",
|
||||
"device.device-edit.148398-81": "设备告警",
|
||||
"device.device-edit.148398-82": "语音对讲",
|
||||
"device.device-edit.148398-83": "计算公式",
|
||||
"device.device-functionlog.399522-0": "请选择设备从机:",
|
||||
"device.device-functionlog.399522-1": "请选择设备从机",
|
||||
"device.device-functionlog.399522-2": "从机地址:$",
|
||||
|
||||
@ -296,9 +296,8 @@
|
||||
"product.product-things-model.142341-50": "请输入单位,例如:℃",
|
||||
"product.product-things-model.142341-51": "步长",
|
||||
"product.product-things-model.142341-52": "请输入步长,例如:1",
|
||||
"product.product-things-model.142341-53": "设备上行数据经计算公式计算后显示 。",
|
||||
"product.product-things-model.142341-53": "设备上行数据经计算公式计算后显示。",
|
||||
"product.product-things-model.142341-54": "公式中的%s为占位符,表示当前物模型的值。",
|
||||
"product.product-things-model.142341-54-1": "使用{标识符}可以引用其他物模型的值,支持多个物模型参与计算。",
|
||||
"product.product-things-model.142341-54-2": "支持运算符:+、-、*、/、%、^(幂运算)、括号()。",
|
||||
"product.product-things-model.142341-55": "如:",
|
||||
"product.product-things-model.142341-56": "加:%s+10",
|
||||
@ -307,10 +306,6 @@
|
||||
"product.product-things-model.142341-59": "除:%s/10",
|
||||
"product.product-things-model.142341-60": "除(保留小数):%s%10.00",
|
||||
"product.product-things-model.142341-61": "保留小数",
|
||||
"product.product-things-model.142341-61-1": "引用其他物模型示例:",
|
||||
"product.product-things-model.142341-61-2": "压力=K*频率平方-常数:1.5*{frequency}^2-100",
|
||||
"product.product-things-model.142341-61-3": "速度=路程/时间:{distance}/{time}",
|
||||
"product.product-things-model.142341-61-4": "混合计算:%s*{factor}+{offset}",
|
||||
"product.product-things-model.142341-62": "控制公式",
|
||||
"product.product-things-model.142341-63": "布尔值",
|
||||
"product.product-things-model.142341-64": "例如:关闭",
|
||||
@ -551,5 +546,9 @@
|
||||
"product.product-modbus-task.894593-67": "提示",
|
||||
"product.product-modbus-task.894593-68": "确定",
|
||||
"product.product-modbus-task.894593-69": "取消",
|
||||
"product.product-modbus-task.894593-70": "已取消"
|
||||
"product.product-modbus-task.894593-70": "已取消",
|
||||
"product.product-things-model.142341-200": "点击符号快速拼接",
|
||||
"product.product-things-model.142341-201": "引用产品物模型标识",
|
||||
"product.product-things-model.142341-202": "暂无可用标识",
|
||||
"product.product-things-model.142341-203": "新增计算模型"
|
||||
}
|
||||
@ -135,6 +135,13 @@
|
||||
<!-- <real-time-status ref="realTimeStatus" :device="form" @statusEvent="getDeviceStatusData($event)" /> -->
|
||||
<running-status ref="runningStatus" :device="form" @statusEvent="getDeviceStatusData($event)" />
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 设备级别物模型 -->
|
||||
<el-tab-pane name="calculatedModel" :disabled="form.deviceId == 0" v-if="form.deviceType !== 3" lazy>
|
||||
<span slot="label"><span style="color: red">* </span>{{$t('device.device-edit.148398-83')}}</span>
|
||||
<device-things-model ref="deviceThingsModel" :device="form" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane
|
||||
name="deviceSub"
|
||||
:disabled="form.deviceId == 0"
|
||||
@ -342,6 +349,7 @@ import deviceVariable from './device-variable';
|
||||
import deviceInlineVideo from './device-inline-video';
|
||||
import deviceFuncLog from './device-functionlog';
|
||||
import deviceSub from './device-sub';
|
||||
import deviceThingsModel from './device-things-model.vue';
|
||||
import vueQr from 'vue-qr';
|
||||
import { loadBMap } from '@/utils/map.js';
|
||||
import { deviceSynchronization, getDevice, addDevice, updateDevice, generatorDeviceNum, getMqttConnect } from '@/api/iot/device';
|
||||
@ -383,6 +391,7 @@ export default {
|
||||
instructionParsing,
|
||||
deviceModbusTask,
|
||||
deviceInlineVideo,
|
||||
deviceThingsModel,
|
||||
},
|
||||
watch: {
|
||||
activeName(val) {
|
||||
|
||||
606
vue/src/views/iot/device/device-things-model.vue
Normal file
606
vue/src/views/iot/device/device-things-model.vue
Normal file
@ -0,0 +1,606 @@
|
||||
<template>
|
||||
<div style="padding-left: 20px">
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['iot:model:add']">
|
||||
新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-refresh" size="mini" @click="getList" v-hasPermi="['iot:model:list']">
|
||||
刷新
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-link type="danger" style="padding-top: 5px" :underline="false">
|
||||
设备级物模型,基于产品物模型进行公式计算
|
||||
</el-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="modelList" size="mini" :border="false">
|
||||
<el-table-column label="物模型名称" align="center" prop="modelName" width="200" />
|
||||
<el-table-column label="标识符" align="center" prop="identifier" />
|
||||
<el-table-column label="图表展示" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.iot_yes_no" :value="scope.row.isChart" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实时监测" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.iot_yes_no" :value="scope.row.isMonitor" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="历史存储" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.iot_yes_no" :value="scope.row.isHistory" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据类型" align="center" prop="datatype" width="80">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.iot_data_type" :value="scope.row.datatype" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据定义" align="left" header-align="center" prop="specs" min-width="150" class-name="specsColor">
|
||||
<template slot-scope="scope">
|
||||
<div v-html="formatSpecsDisplay(scope.row.specs)"></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计算公式" align="left" prop="formula" min-width="180">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip v-if="scope.row.formula" :content="scope.row.formula" placement="top" effect="light">
|
||||
<span class="formula-tag">{{ scope.row.formula }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="center" prop="modelOrder" width="80" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleEdit(scope.row)" v-hasPermi="['iot:model:edit']">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['iot:model:remove']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<!-- 添加或修改物模型对话框 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="600px" append-to-body v-dialogDrag>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="物模型名称" prop="modelName">
|
||||
<el-input v-model="form.modelName" placeholder="请输入物模型名称" style="width: 385px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标识符" prop="identifier">
|
||||
<el-input v-model="form.identifier" placeholder="请输入标识符" style="width: 385px" :disabled="!!form.modelId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="modelOrder">
|
||||
<el-input v-model="form.modelOrder" placeholder="请输入排序" type="number" style="width: 385px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模型特性" prop="property">
|
||||
<el-checkbox name="isChart" label="图表展示" v-model="form.isChart" :true-label="1" :false-label="0"></el-checkbox>
|
||||
<el-checkbox name="isMonitor" label="实时监测" v-model="form.isMonitor" :true-label="1" :false-label="0"></el-checkbox>
|
||||
<el-checkbox name="isHistory" label="历史存储" v-model="form.isHistory" :true-label="1" :false-label="0"></el-checkbox>
|
||||
</el-form-item>
|
||||
<el-divider></el-divider>
|
||||
<el-form-item label="数据类型" prop="datatype">
|
||||
<el-select v-model="form.datatype" placeholder="请选择数据类型" style="width: 175px">
|
||||
<el-option key="integer" label="整数(integer)" value="integer"></el-option>
|
||||
<el-option key="decimal" label="小数(decimal)" value="decimal"></el-option>
|
||||
<el-option key="string" label="字符串(string)" value="string"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="form.datatype == 'integer' || form.datatype == 'decimal'">
|
||||
<el-form-item label="取值范围">
|
||||
<el-row>
|
||||
<el-col :span="9">
|
||||
<el-input v-model="specsForm.min" placeholder="最小值" type="number" />
|
||||
</el-col>
|
||||
<el-col :span="2" align="center">~</el-col>
|
||||
<el-col :span="9">
|
||||
<el-input v-model="specsForm.max" placeholder="最大值" type="number" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<el-form-item label="单位">
|
||||
<el-input v-model="specsForm.unit" placeholder="请输入单位,如:℃、%" style="width: 385px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="步长">
|
||||
<el-input v-model="specsForm.step" placeholder="请输入步长" type="number" style="width: 385px" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div v-if="form.datatype == 'string'">
|
||||
<el-form-item label="数据长度">
|
||||
<el-row>
|
||||
<el-col :span="9">
|
||||
<el-input v-model="specsForm.maxLength" placeholder="请输入最大长度" type="number" />
|
||||
</el-col>
|
||||
<el-col :span="14" :offset="1">字节(Byte),默认1024</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<el-form-item label="计算公式" prop="formula">
|
||||
<template slot="label">
|
||||
<span slot="label">{{$t('device.device-edit.148398-83')}}</span>
|
||||
<el-tooltip style="cursor: pointer" effect="light" placement="top">
|
||||
<div slot="content">
|
||||
用于对原始值进行计算后存储和显示<br />
|
||||
公式中使用产品物模型标识符,并用大括号包裹<br />
|
||||
支持运算符:+ - * / % ^(次方) sqrt(开方)<br />
|
||||
<br />
|
||||
<strong>示例:</strong><br />
|
||||
压力=K*频率平方-常数:1.5*{frequency}^2-100
|
||||
</div>
|
||||
<i class="el-icon-question" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<div class="formula-editor-container">
|
||||
<!-- 运算符按钮组 -->
|
||||
<div class="operator-section">
|
||||
<span class="section-label">运算符:</span>
|
||||
<div class="operator-buttons">
|
||||
<el-button style="color: #f56c6c" size="mini" title="加法" @click="insertOperator('+')">+</el-button>
|
||||
<el-button style="color: #f56c6c" size="mini" title="减法" @click="insertOperator('-')">−</el-button>
|
||||
<el-button style="color: #f56c6c" size="mini" title="乘法" @click="insertOperator('*')">×</el-button>
|
||||
<el-button style="color: #f56c6c" size="mini" title="除法" @click="insertOperator('/')">÷</el-button>
|
||||
<el-button style="color: #f56c6c" size="mini" title="次方" @click="insertOperator('^')">^</el-button>
|
||||
<el-button style="color: #f56c6c" size="mini" title="开方" @click="insertOperator('sqrt')">sqrt</el-button>
|
||||
<el-button style="color: #f56c6c" size="mini" title="取余" @click="insertOperator('%')">%</el-button>
|
||||
<el-button style="color: #f56c6c" size="mini" title="左括号" @click="insertOperator('(')">(</el-button>
|
||||
<el-button style="color: #f56c6c" size="mini" title="右括号" @click="insertOperator(')')">)</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 物模型变量区域 -->
|
||||
<div class="variable-section" v-loading="loadingModels">
|
||||
<span class="section-label">产品通用物模型变量:</span>
|
||||
<div class="variable-tags">
|
||||
<el-tag
|
||||
v-for="model in productModels"
|
||||
:key="model.identifier"
|
||||
size="small"
|
||||
type="primary"
|
||||
effect="plain"
|
||||
class="var-tag"
|
||||
@click="insertVariable(model.identifier)"
|
||||
:title="model.identifier"
|
||||
>
|
||||
{{ model.modelName }}
|
||||
</el-tag>
|
||||
<span v-if="productModels.length === 0 && !loadingModels" class="no-data">暂无可用物模型</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 输入框和清空按钮 -->
|
||||
<div class="formula-input-section">
|
||||
<el-input v-model="form.formula" type="textarea" :rows="2" placeholder="输入或点击上方运算符、物模型变量构建公式" class="formula-input" />
|
||||
<el-button type="danger" plain size="small" title="清空公式" @click="form.formula = ''" class="clear-btn">清空</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm" v-show="form.modelId">修 改</el-button>
|
||||
<el-button type="primary" @click="submitForm" v-show="!form.modelId">新 增</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.specsColor {
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
/* 公式编辑器容器 */
|
||||
.formula-editor-container {
|
||||
width: 420px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
padding: 14px;
|
||||
background: linear-gradient(to bottom, #fafbfc, #ffffff);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* 运算符区域 */
|
||||
.operator-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.operator-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.operator-buttons .el-button {
|
||||
min-width: 38px;
|
||||
height: 32px;
|
||||
font-weight: 600;
|
||||
padding: 0 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.operator-buttons .el-button--default {
|
||||
background: #fff;
|
||||
border-color: #dcdfe6;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.operator-buttons .el-button--default:hover {
|
||||
color: #409eff;
|
||||
border-color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
/* 物模型变量区域 */
|
||||
.variable-section {
|
||||
margin-bottom: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.variable-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
max-height: 80px;
|
||||
overflow-y: auto;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.var-tag {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
padding: 0 10px;
|
||||
height: 26px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.var-tag:hover {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
border-color: #409eff;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(64, 158, 255, 0.35);
|
||||
}
|
||||
|
||||
.no-data {
|
||||
color: #c0c4cc;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 输入框和清空按钮区域 */
|
||||
.formula-input-section {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.formula-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.formula-input ::v-deep .el-textarea__inner {
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||
font-size: 13px;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
border-color: #dcdfe6;
|
||||
}
|
||||
|
||||
.formula-input ::v-deep .el-textarea__inner:focus {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
height: 54px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.clear-btn:hover {
|
||||
background-color: #fef0f0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { listModel, getModel, addModel, updateModel, delModel } from '@/api/iot/model';
|
||||
|
||||
export default {
|
||||
name: 'DeviceThingsModel',
|
||||
dicts: ['iot_data_type', 'iot_yes_no'],
|
||||
props: {
|
||||
device: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
loadingModels: false,
|
||||
dialogVisible: false,
|
||||
dialogTitle: '',
|
||||
total: 0,
|
||||
modelList: [],
|
||||
productModels: [],
|
||||
queryParams: {
|
||||
productId: 0,
|
||||
deviceId: 0,
|
||||
isCalculated: 1,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
form: {
|
||||
modelId: null,
|
||||
modelName: '',
|
||||
identifier: '',
|
||||
productId: 0,
|
||||
productName: '',
|
||||
deviceId: 0,
|
||||
serialNumber: '',
|
||||
tenantId: 0,
|
||||
tenantName: '',
|
||||
type: 1,
|
||||
datatype: 'decimal',
|
||||
formula: '',
|
||||
isCalculated: 1,
|
||||
specs: '',
|
||||
isChart: 1,
|
||||
isHistory: 1,
|
||||
isMonitor: 1,
|
||||
isReadonly: 1,
|
||||
modelOrder: 0,
|
||||
remark: '',
|
||||
},
|
||||
specsForm: {
|
||||
type: 'decimal',
|
||||
max: 100,
|
||||
min: 0,
|
||||
step: 1,
|
||||
unit: '',
|
||||
maxLength: 1024,
|
||||
},
|
||||
rules: {
|
||||
modelName: [{ required: true, message: '请输入物模型名称', trigger: 'blur' }],
|
||||
identifier: [
|
||||
{ required: true, message: '请输入标识符', trigger: 'blur' },
|
||||
{ pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/, message: '以字母开头,只含字母、数字、下划线', trigger: 'blur' },
|
||||
],
|
||||
datatype: [{ required: true, message: '请选择数据类型', trigger: 'change' }],
|
||||
formula: [{ required: true, message: '请输入计算公式', trigger: 'blur' }],
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
device: {
|
||||
handler(val) {
|
||||
if (val && val.deviceId) {
|
||||
this.queryParams.productId = val.productId || 0;
|
||||
this.queryParams.deviceId = val.deviceId || 0;
|
||||
this.getList();
|
||||
this.loadProductModels();
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** 查询设备计算物模型列表 */
|
||||
getList() {
|
||||
if (!this.queryParams.deviceId) return;
|
||||
this.loading = true;
|
||||
listModel(this.queryParams)
|
||||
.then((response) => {
|
||||
this.modelList = response.rows || [];
|
||||
this.total = response.total || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 加载产品物模型(用于公式变量选择) */
|
||||
loadProductModels() {
|
||||
if (!this.device.productId) return;
|
||||
this.loadingModels = true;
|
||||
listModel({
|
||||
productId: this.device.productId,
|
||||
deviceId: 0,
|
||||
pageNum: 1,
|
||||
pageSize: 200,
|
||||
})
|
||||
.then((response) => {
|
||||
// 过滤掉纯计算物模型(只保留设备上报的物模型)
|
||||
this.productModels = (response.rows || []).filter((m) => m.isCalculated !== 1);
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadingModels = false;
|
||||
});
|
||||
},
|
||||
/** 表单重置 */
|
||||
resetForm() {
|
||||
this.form = {
|
||||
modelId: null,
|
||||
modelName: '',
|
||||
identifier: '',
|
||||
productId: this.device.productId || 0,
|
||||
productName: this.device.productName || '',
|
||||
deviceId: this.device.deviceId || 0,
|
||||
serialNumber: this.device.serialNumber || '',
|
||||
tenantId: this.device.tenantId || 0,
|
||||
tenantName: this.device.tenantName || '',
|
||||
type: 1,
|
||||
datatype: 'decimal',
|
||||
formula: '',
|
||||
isCalculated: 1,
|
||||
specs: '',
|
||||
isChart: 1,
|
||||
isHistory: 1,
|
||||
isMonitor: 1,
|
||||
isReadonly: 1,
|
||||
modelOrder: 0,
|
||||
remark: '',
|
||||
};
|
||||
this.specsForm = {
|
||||
type: 'decimal',
|
||||
max: 100,
|
||||
min: 0,
|
||||
step: 1,
|
||||
unit: '',
|
||||
maxLength: 1024,
|
||||
};
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.resetForm();
|
||||
this.dialogTitle = '新增计算物模型';
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.form && this.$refs.form.clearValidate();
|
||||
});
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleEdit(row) {
|
||||
this.resetForm();
|
||||
this.dialogTitle = '修改计算物模型';
|
||||
getModel(row.modelId).then((response) => {
|
||||
const data = response.data || {};
|
||||
this.form = { ...this.form, ...data };
|
||||
if (data.specs) {
|
||||
try {
|
||||
this.specsForm = JSON.parse(data.specs);
|
||||
} catch (e) {
|
||||
console.error('解析specs失败', e);
|
||||
}
|
||||
}
|
||||
this.dialogVisible = true;
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
this.$modal
|
||||
.confirm(`是否确认删除物模型"${row.modelName}"?`)
|
||||
.then(() => delModel(row.modelId))
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess('删除成功');
|
||||
this.getList();
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
/** 插入运算符 */
|
||||
insertOperator(op) {
|
||||
this.form.formula = (this.form.formula || '') + op;
|
||||
},
|
||||
/** 插入变量(标识符使用大括号包裹) */
|
||||
insertVariable(identifier) {
|
||||
this.form.formula = (this.form.formula || '') + `{${identifier}}`;
|
||||
},
|
||||
/** 格式化物模型specs */
|
||||
formatThingsSpecs() {
|
||||
var data = {};
|
||||
data.type = this.form.datatype;
|
||||
if (this.form.datatype == 'integer' || this.form.datatype == 'decimal') {
|
||||
data.min = Number(this.specsForm.min ? this.specsForm.min : 0);
|
||||
data.max = Number(this.specsForm.max ? this.specsForm.max : 100);
|
||||
data.unit = this.specsForm.unit ? this.specsForm.unit : '';
|
||||
data.step = Number(this.specsForm.step ? this.specsForm.step : 1);
|
||||
} else if (this.form.datatype == 'string') {
|
||||
data.maxLength = Number(this.specsForm.maxLength ? this.specsForm.maxLength : 1024);
|
||||
}
|
||||
return JSON.stringify(data);
|
||||
},
|
||||
/** 格式化显示数据定义 */
|
||||
formatSpecsDisplay(json) {
|
||||
if (json == null || json == undefined) {
|
||||
return;
|
||||
}
|
||||
let specs = JSON.parse(json);
|
||||
if (specs.type === 'integer' || specs.type === 'decimal') {
|
||||
return (
|
||||
`<span style='width:50%;display:inline-block;'>最大值:<span style="color:#F56C6C">` +
|
||||
specs.max +
|
||||
`</span></span>最小值:<span style="color:#F56C6C">` +
|
||||
specs.min +
|
||||
`</span><br /><span style='width:50%;display:inline-block;'>步长:<span style="color:#F56C6C">` +
|
||||
specs.step +
|
||||
`</span></span>单位:<span style="color:#F56C6C">` +
|
||||
specs.unit
|
||||
);
|
||||
} else if (specs.type === 'string') {
|
||||
return `数据长度:<span style="color:#F56C6C">` + specs.maxLength + '</span>';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return;
|
||||
|
||||
// 格式化specs
|
||||
this.form.specs = this.formatThingsSpecs();
|
||||
this.form.isCalculated = 1;
|
||||
this.form.isReadonly = 1;
|
||||
this.form.type = 1;
|
||||
// 去除公式中的空格
|
||||
if (this.form.formula) {
|
||||
this.form.formula = this.form.formula.replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
const request = this.form.modelId ? updateModel(this.form) : addModel(this.form);
|
||||
request.then(() => {
|
||||
this.$modal.msgSuccess(this.form.modelId ? '修改成功' : '新增成功');
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.formula-tag {
|
||||
display: inline-block;
|
||||
max-width: 140px;
|
||||
color: #d85a5a;
|
||||
font-family: 'SF Mono', Monaco, Menlo, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.3px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
padding: 4px 10px;
|
||||
border: 1px solid rgba(216, 90, 90, 0.35);
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, rgba(255, 235, 235, 0.8) 0%, rgba(255, 245, 245, 0.6) 100%);
|
||||
box-shadow: 0 1px 3px rgba(216, 90, 90, 0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@ -74,6 +74,8 @@ export default {
|
||||
productId: 0,
|
||||
// 1-属性,2-功能,3-事件,4-属性和功能
|
||||
type: 4,
|
||||
// 产品管理页面只显示设备上报的物模型,不显示纯计算物模型
|
||||
isCalculated: 0,
|
||||
},
|
||||
form: {},
|
||||
// 产品
|
||||
|
||||
@ -195,7 +195,7 @@
|
||||
<span style="color: red" v-hasPermi="['iot:model:list']">*</span>
|
||||
{{ $t('product.product-edit.473153-38') }}
|
||||
</span>
|
||||
<product-things-model ref="productThingsModel" :product="form" @updateModel="updateModel" />
|
||||
<product-things-model ref="productThingsModel" :product="form" :isCalculated="0" @updateModel="updateModel" />
|
||||
</el-tab-pane>
|
||||
<!-- 子产品 -->
|
||||
<el-tab-pane name="productSub" :disabled="form.productId == 0" v-if="form.deviceType == 2 && (form.protocolCode == 'MODBUS-RTU' || form.protocolCode == 'MODBUS-TCP')" lazy>
|
||||
|
||||
@ -401,6 +401,7 @@ export default {
|
||||
pageNum: 1,
|
||||
pageSize: 1000,
|
||||
productId: 0,
|
||||
isCalculated: 0,
|
||||
},
|
||||
thingsTotal: 0,
|
||||
// 表单参数
|
||||
|
||||
@ -196,8 +196,6 @@
|
||||
<br />
|
||||
{{ $t('product.product-things-model.142341-54') }}
|
||||
<br />
|
||||
{{ $t('product.product-things-model.142341-54-1') }}
|
||||
<br />
|
||||
{{ $t('product.product-things-model.142341-54-2') }}
|
||||
<br />
|
||||
<br />
|
||||
@ -211,16 +209,7 @@
|
||||
<br />
|
||||
{{ $t('product.product-things-model.142341-59') }}
|
||||
<br />
|
||||
{{ $t('product.product-things-model.142341-60') }}({{ $t('product.product-things-model.142341-61') }}):%s%10.00
|
||||
<br />
|
||||
<br />
|
||||
<strong>{{ $t('product.product-things-model.142341-61-1') }}</strong>
|
||||
<br />
|
||||
{{ $t('product.product-things-model.142341-61-2') }}
|
||||
<br />
|
||||
{{ $t('product.product-things-model.142341-61-3') }}
|
||||
<br />
|
||||
{{ $t('product.product-things-model.142341-61-4') }}
|
||||
{{ $t('product.product-things-model.142341-60') }}({{ $t('product.product-things-model.142341-61') }})
|
||||
<br />
|
||||
</div>
|
||||
<i class="el-icon-question" />
|
||||
@ -417,6 +406,10 @@ export default {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
isCalculated: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 获取到父组件传递的productId后,刷新列表
|
||||
@ -425,6 +418,10 @@ export default {
|
||||
if (this.productInfo && this.productInfo.productId != 0) {
|
||||
this.queryParams.productId = this.productInfo.productId;
|
||||
this.productId = this.productInfo.productId;
|
||||
// 设置isCalculated查询条件(产品管理页面只显示设备上报的物模型,不显示纯计算物模型)
|
||||
if (this.isCalculated !== null) {
|
||||
this.queryParams.isCalculated = this.isCalculated;
|
||||
}
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user