20260711:1、物模型纵向计算
This commit is contained in:
parent
ea8616c649
commit
95d35341ac
@ -8,11 +8,8 @@ TARGET_PORT = 8888 # 请根据实际情况修改
|
|||||||
|
|
||||||
# 元数组 modbus 指令
|
# 元数组 modbus 指令
|
||||||
MODBUS_DATA_HEX = (
|
MODBUS_DATA_HEX = (
|
||||||
"7e80DD33D43DFC5B7e",
|
"7e80D20C4C6A55D57e",
|
||||||
"010320413d0d7541bacb2dbf42019a3f6bdb33c2b19c73c0d39f304100a253c3f9f2187f3a",
|
"01030400110018AA3C"
|
||||||
"020320414056d541b8ad60c02d4acd3fa708cdc2adfe5ac1bcf64c413630c4c3f9a81af11d",
|
|
||||||
"030320413fc48241b919bcc0006ecd3ff874cdc2ae6c0ac18c11e041877c67c3f9b431a049",
|
|
||||||
"040320413f56c341b0362ebf48b333403e3200c2addb5cc0daec4341cf60bdc3f9a40fae0b"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 深部位移计
|
# 深部位移计
|
||||||
|
|||||||
@ -133,7 +133,7 @@ public class CRC16Utils {
|
|||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
String hex = "006000000000";
|
String hex = "01030400110018AA3C";
|
||||||
byte[] bytes = ByteUtils.hexToByte(hex);
|
byte[] bytes = ByteUtils.hexToByte(hex);
|
||||||
String crc = getCRC(bytes);
|
String crc = getCRC(bytes);
|
||||||
System.out.println(crc);
|
System.out.println(crc);
|
||||||
|
|||||||
@ -363,7 +363,7 @@ public class TSLCacheImpl implements ITSLCache {
|
|||||||
// 从数据库查询设备级计算物模型
|
// 从数据库查询设备级计算物模型
|
||||||
ThingsModel model = new ThingsModel();
|
ThingsModel model = new ThingsModel();
|
||||||
model.setSerialNumber(serialNumber);
|
model.setSerialNumber(serialNumber);
|
||||||
model.setIsCalculated(1); // 查询设备级物模型
|
model.setIsCalculatedList(Arrays.asList(1,2)); // 查询设备级物模型
|
||||||
List<ThingsModel> models = thingsModelService.selectThingsModelList(model);
|
List<ThingsModel> models = thingsModelService.selectThingsModelList(model);
|
||||||
if (CollectionUtils.isEmpty(models)) {
|
if (CollectionUtils.isEmpty(models)) {
|
||||||
// 清除可能存在的旧缓存
|
// 清除可能存在的旧缓存
|
||||||
|
|||||||
@ -109,12 +109,14 @@ public class ThingsModel extends BaseEntity {
|
|||||||
private String formula;
|
private String formula;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 物模型来源类型(0-设备上报,1-纯计算,2-其他预留)
|
* 物模型来源类型(0-设备上报,1-纯计算(横向计算),2-纵向计算)
|
||||||
* 纯计算物模型:计算公式包含{},依赖其他物模型计算值
|
* 纯计算物模型:计算公式包含{},依赖其他物模型计算值
|
||||||
*/
|
*/
|
||||||
@ApiModelProperty(value = "物模型来源类型", notes = "(0-设备上报,1-纯计算,2-其他预留)")
|
@ApiModelProperty(value = "物模型来源类型", notes = "(0-设备上报,1-纯计算(横向计算),2-纵向计算)")
|
||||||
private Integer isCalculated;
|
private Integer isCalculated;
|
||||||
|
|
||||||
|
private List<Integer> isCalculatedList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据定义
|
* 数据定义
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -81,7 +81,7 @@ public class ThingsModelValueItem {
|
|||||||
private String formula;
|
private String formula;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 物模型来源类型(0-设备上报,1-纯计算,2-其他预留)
|
* 物模型来源类型(0-设备上报,1-纯计算(横向计算),2-纵向计算)
|
||||||
*/
|
*/
|
||||||
private Integer isCalculated;
|
private Integer isCalculated;
|
||||||
|
|
||||||
|
|||||||
@ -102,9 +102,7 @@ public class DcpPlatformAdapter {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
String pushPath = resolvePushPath(mapping, pushInterface);
|
String pushPath = resolvePushPath(mapping, pushInterface);
|
||||||
handleNormalPush(
|
handleNormalPush(platform, mapping, reportSerialNumber, payload, pushPath, interfaceCode, requestSchema);
|
||||||
platform, mapping, reportSerialNumber, payload, pushPath,
|
|
||||||
interfaceCode, requestSchema);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String serialNumber = StringUtils.hasText(reportSerialNumber)
|
String serialNumber = StringUtils.hasText(reportSerialNumber)
|
||||||
? reportSerialNumber : mapping.getSerialNumber();
|
? reportSerialNumber : mapping.getSerialNumber();
|
||||||
@ -151,8 +149,7 @@ public class DcpPlatformAdapter {
|
|||||||
// 无数组字段 → 简单推送,使用子设备 platformDeviceId 和主表 fieldMapping
|
// 无数组字段 → 简单推送,使用子设备 platformDeviceId 和主表 fieldMapping
|
||||||
if (!hasArrayField(requestSchema)) {
|
if (!hasArrayField(requestSchema)) {
|
||||||
// 如果只有一个设备完全可以走
|
// 如果只有一个设备完全可以走
|
||||||
handleSubDeviceSimplePush(platform, mapping, subMapping, reportSerialNumber,
|
handleSubDeviceSimplePush(platform, mapping, subMapping, reportSerialNumber, payload, pushPath, interfaceCode, requestSchema);
|
||||||
payload, pushPath, interfaceCode, requestSchema);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,12 +345,10 @@ public class DcpPlatformAdapter {
|
|||||||
|
|
||||||
int replayCount = 0;
|
int replayCount = 0;
|
||||||
for (PlatformDeviceSubMapping monitor : subMappings) {
|
for (PlatformDeviceSubMapping monitor : subMappings) {
|
||||||
if (monitor == null || !monitor.isMonitor()
|
if (monitor == null || !monitor.isMonitor() || !StringUtils.hasText(monitor.getSubSerialNumber())) {
|
||||||
|| !StringUtils.hasText(monitor.getSubSerialNumber())) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
JSONObject monitorData = bufferService.getMonitorDataIfFresh(
|
JSONObject monitorData = bufferService.getMonitorDataIfFresh(platform.getId(), fieldName, monitor.getSubSerialNumber(), reportIntervalSec);
|
||||||
platform.getId(), fieldName, monitor.getSubSerialNumber(), reportIntervalSec);
|
|
||||||
if (monitorData == null) {
|
if (monitorData == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -379,8 +374,7 @@ public class DcpPlatformAdapter {
|
|||||||
JSONArray pushBody = buildArrayPushBody(mapping, bodyFields, fieldName,
|
JSONArray pushBody = buildArrayPushBody(mapping, bodyFields, fieldName,
|
||||||
sensorData, monitor.getPlatformDeviceId());
|
sensorData, monitor.getPlatformDeviceId());
|
||||||
|
|
||||||
PlatformPushLog logEntry = createLogEntry(platform, mapping, interfaceCode,
|
PlatformPushLog logEntry = createLogEntry(platform, mapping, interfaceCode, monitor.getSubSerialNumber());
|
||||||
monitor.getSubSerialNumber());
|
|
||||||
doHttpPost(platform, baseUrl, JSON.toJSONString(pushBody), logEntry);
|
doHttpPost(platform, baseUrl, JSON.toJSONString(pushBody), logEntry);
|
||||||
pushLogService.asyncSave(logEntry);
|
pushLogService.asyncSave(logEntry);
|
||||||
if (logEntry.getStatus() != null && logEntry.getStatus() == 1) {
|
if (logEntry.getStatus() != null && logEntry.getStatus() == 1) {
|
||||||
@ -1117,8 +1111,7 @@ public class DcpPlatformAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentIndex == -1) {
|
if (currentIndex == -1) {
|
||||||
log.warn("[DCP] 当前子设备 {} 不在监测点列表中 mappingId={}",
|
log.warn("[DCP] 当前子设备 {} 不在监测点列表中 mappingId={}", reportSerialNumber, mapping.getId());
|
||||||
reportSerialNumber, mapping.getId());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1156,8 +1149,7 @@ public class DcpPlatformAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String sensorId = resolveSubDeviceSensorId(sub);
|
String sensorId = resolveSubDeviceSensorId(sub);
|
||||||
JSONObject sensorDataItem = buildArrayItem(
|
JSONObject sensorDataItem = buildArrayItem(platform, mapping, requestSchema, reportData, fieldName, itemFieldMappings, sensorId);
|
||||||
platform, mapping, requestSchema, reportData, fieldName, itemFieldMappings, sensorId);
|
|
||||||
if (sensorDataItem == null || sensorDataItem.isEmpty()) {
|
if (sensorDataItem == null || sensorDataItem.isEmpty()) {
|
||||||
log.warn("[DCP] 子设备 {} 数据映射为空,跳过其 sensorData", subSn);
|
log.warn("[DCP] 子设备 {} 数据映射为空,跳过其 sensorData", subSn);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -691,9 +691,8 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
log.warn("=>查询物模型为空[{}],标识符:[{}]", input.getProductId(), identity);
|
log.warn("=>查询物模型为空[{}],标识符:[{}]", input.getProductId(), identity);
|
||||||
continue; // 跳过当前物模型的处理
|
continue; // 跳过当前物模型的处理
|
||||||
}
|
}
|
||||||
// 获取设备实时值
|
// 获取设备上一次缓存值
|
||||||
ValueItem cacheIdentifier = itslValueCache.getCacheIdentifier(input.getProductId(), serialNumber,
|
ValueItem cacheIdentifier = itslValueCache.getCacheIdentifier(input.getProductId(), serialNumber, thingModels.getId());
|
||||||
thingModels.getId());
|
|
||||||
if (Objects.isNull(cacheIdentifier)) {
|
if (Objects.isNull(cacheIdentifier)) {
|
||||||
log.warn("=>查询物模型值为空[{}],标识符:[{}]", input.getProductId(), identity);
|
log.warn("=>查询物模型值为空[{}],标识符:[{}]", input.getProductId(), identity);
|
||||||
}
|
}
|
||||||
@ -719,12 +718,17 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
String refValue = thingsModelValueMap.get(refIdentifier);
|
String refValue = thingsModelValueMap.get(refIdentifier);
|
||||||
if (refValue != null && !refValue.isEmpty()) {
|
if (refValue != null && !refValue.isEmpty()) {
|
||||||
params.put("{" + refIdentifier + "}", refValue);
|
params.put("{" + refIdentifier + "}", refValue);
|
||||||
|
} else {
|
||||||
|
// 纵向计算物模型
|
||||||
|
if (Integer.valueOf(2).equals(thingModels.getIsCalculated())) {
|
||||||
|
params.put("{" + refIdentifier + "}", cacheIdentifier.getValue());
|
||||||
} else {
|
} else {
|
||||||
params.put("{" + refIdentifier + "}", "0");
|
params.put("{" + refIdentifier + "}", "0");
|
||||||
log.warn("=>计算公式引用的物模型值不存在[{}],标识符:[{}],使用默认值0", input.getProductId(), refIdentifier);
|
log.warn("=>计算公式引用的物模型值不存在[{}],标识符:[{}],使用默认值0", input.getProductId(), refIdentifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 执行计算
|
// 执行计算
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -144,6 +144,12 @@
|
|||||||
<if test="isCalculated != null ">
|
<if test="isCalculated != null ">
|
||||||
AND is_calculated = #{isCalculated}
|
AND is_calculated = #{isCalculated}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="isCalculatedList != null ">
|
||||||
|
AND is_calculated IN
|
||||||
|
<foreach collection="isCalculatedList" item="item" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
</if>
|
||||||
<if test="modelIdList != null ">
|
<if test="modelIdList != null ">
|
||||||
model_id IN
|
model_id IN
|
||||||
<foreach collection="modelIdList" item="item" separator="," open="(" close=")">
|
<foreach collection="modelIdList" item="item" separator="," open="(" close=")">
|
||||||
|
|||||||
@ -12,7 +12,7 @@ VUE_APP_BASE_API = /dev-api
|
|||||||
VUE_APP_SERVER_API_URL = http://localhost:8080
|
VUE_APP_SERVER_API_URL = http://localhost:8080
|
||||||
|
|
||||||
# MQTT WebSocket 服务器地址
|
# MQTT WebSocket 服务器地址
|
||||||
VUE_APP_MQTT_SERVER_URL = ws://localhost:8083/mqtt
|
VUE_APP_MQTT_SERVER_URL = ws://47.121.180.229:8083/mqtt
|
||||||
|
|
||||||
# 百度地图 API Key
|
# 百度地图 API Key
|
||||||
VUE_APP_BAI_DU_AK = s9mUXN9a7bKgVu6U6Z3bXhKSxtz97YPI
|
VUE_APP_BAI_DU_AK = s9mUXN9a7bKgVu6U6Z3bXhKSxtz97YPI
|
||||||
|
|||||||
@ -21,6 +21,13 @@
|
|||||||
<el-table v-loading="loading" :data="modelList" size="mini" :border="false">
|
<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="modelName" width="200" />
|
||||||
<el-table-column label="标识符" align="center" prop="identifier" />
|
<el-table-column label="标识符" align="center" prop="identifier" />
|
||||||
|
<el-table-column label="计算类型" align="center" width="90">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag :type="scope.row.isCalculated === 2 ? 'warning' : 'primary'" size="mini">
|
||||||
|
{{ scope.row.isCalculated === 2 ? '纵向累计' : '横向计算' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="图表展示" align="center" width="80">
|
<el-table-column label="图表展示" align="center" width="80">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<dict-tag :options="dict.type.iot_yes_no" :value="scope.row.isChart" />
|
<dict-tag :options="dict.type.iot_yes_no" :value="scope.row.isChart" />
|
||||||
@ -80,6 +87,22 @@
|
|||||||
<el-form-item label="排序" prop="modelOrder">
|
<el-form-item label="排序" prop="modelOrder">
|
||||||
<el-input v-model="form.modelOrder" placeholder="请输入排序" type="number" style="width: 580px" />
|
<el-input v-model="form.modelOrder" placeholder="请输入排序" type="number" style="width: 580px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="计算类型" prop="isCalculated">
|
||||||
|
<el-radio-group v-model="form.isCalculated">
|
||||||
|
<el-radio :label="1">
|
||||||
|
横向计算
|
||||||
|
<el-tooltip content="使用同一批上报数据中的物模型值计算" placement="top">
|
||||||
|
<i class="el-icon-question" style="color:#909399;margin-left:5px;cursor:help;"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="2">
|
||||||
|
纵向累计
|
||||||
|
<el-tooltip content="使用目标物模型缓存值 + 本次源物模型上报值" placement="top">
|
||||||
|
<i class="el-icon-question" style="color:#909399;margin-left:5px;cursor:help;"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="模型特性" prop="property">
|
<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="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="isMonitor" label="实时监测" v-model="form.isMonitor" :true-label="1" :false-label="0"></el-checkbox>
|
||||||
@ -130,12 +153,14 @@
|
|||||||
<span slot="label">{{$t('device.device-edit.148398-83')}}</span>
|
<span slot="label">{{$t('device.device-edit.148398-83')}}</span>
|
||||||
<el-tooltip style="cursor: pointer" effect="light" placement="top">
|
<el-tooltip style="cursor: pointer" effect="light" placement="top">
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
用于对原始值进行计算后存储和显示<br />
|
横向计算:使用同一批上报数据中的物模型值<br />
|
||||||
|
纵向累计:使用 {last} 表示当前物模型上一次缓存累计值<br />
|
||||||
公式中使用产品物模型标识符,并用大括号包裹<br />
|
公式中使用产品物模型标识符,并用大括号包裹<br />
|
||||||
支持运算符:+ - * / % ^(次方) sqrt(开方)<br />
|
支持运算符:+ - * / % ^(次方) sqrt(开方)<br />
|
||||||
<br />
|
<br />
|
||||||
<strong>示例:</strong><br />
|
<strong>示例:</strong><br />
|
||||||
压力=K*频率平方-常数:1.5*{frequency}^2-100
|
横向计算:1.5*{frequency}^2-100<br />
|
||||||
|
纵向累计:{last}+{rain}
|
||||||
</div>
|
</div>
|
||||||
<i class="el-icon-question" />
|
<i class="el-icon-question" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
@ -158,8 +183,24 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 物模型变量区域:网关设备按从机展示,便于插值计算 -->
|
<!-- 物模型变量区域:网关设备按从机展示,便于插值计算 -->
|
||||||
<div class="variable-section" v-loading="loadingModels">
|
<div class="variable-section" v-loading="loadingModels">
|
||||||
<span class="section-label">{{ isGatewayDevice ? '从机物模型变量(点击插入,用于跨设备插值如 从机1-从机2):' : '产品通用物模型变量:' }}</span>
|
<span class="section-label">{{ form.isCalculated === 2 ? '纵向累计变量:' : (isGatewayDevice ? '从机物模型变量(点击插入,用于跨设备插值如 从机1-从机2):' : '产品通用物模型变量:') }}</span>
|
||||||
<template v-if="isGatewayDevice && gatewayVariableGroups.length">
|
<div v-if="form.isCalculated === 2" class="variable-tags">
|
||||||
|
<el-tag size="small" type="warning" effect="plain" class="var-tag" @click="insertVariable('last')" title="{last}">上次累计值</el-tag>
|
||||||
|
<el-tag
|
||||||
|
v-for="model in productModels"
|
||||||
|
:key="model.identifier"
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
effect="plain"
|
||||||
|
class="var-tag"
|
||||||
|
@click="setVerticalFormula(model.identifier)"
|
||||||
|
:title="`{last}+{${model.identifier}}`"
|
||||||
|
>
|
||||||
|
{{ model.modelName }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-if="productModels.length === 0 && !loadingModels" class="no-data">暂无可用物模型</span>
|
||||||
|
</div>
|
||||||
|
<template v-else-if="isGatewayDevice && gatewayVariableGroups.length">
|
||||||
<div v-for="grp in gatewayVariableGroups" :key="grp.slaveId" class="variable-group">
|
<div v-for="grp in gatewayVariableGroups" :key="grp.slaveId" class="variable-group">
|
||||||
<span class="group-label">从机{{ grp.slaveId }}:</span>
|
<span class="group-label">从机{{ grp.slaveId }}:</span>
|
||||||
<el-tag
|
<el-tag
|
||||||
@ -194,7 +235,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 输入框和清空按钮 -->
|
<!-- 输入框和清空按钮 -->
|
||||||
<div class="formula-input-section">
|
<div class="formula-input-section">
|
||||||
<el-input ref="formulaInput" v-model="form.formula" type="textarea" :rows="2" placeholder="输入或点击上方运算符、物模型变量构建公式" class="formula-input" />
|
<el-input ref="formulaInput" v-model="form.formula" type="textarea" :rows="2" :placeholder="form.isCalculated === 2 ? '请选择源物模型生成累计公式,如:{last}+{rain}' : '输入或点击上方运算符、物模型变量构建公式'" class="formula-input" />
|
||||||
<el-button type="danger" plain size="small" title="清空公式" @click="clearFormula" class="clear-btn">清空</el-button>
|
<el-button type="danger" plain size="small" title="清空公式" @click="clearFormula" class="clear-btn">清空</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -241,6 +282,12 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-tip {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
.operator-buttons {
|
.operator-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
@ -427,7 +474,6 @@ export default {
|
|||||||
queryParams: {
|
queryParams: {
|
||||||
productId: 0,
|
productId: 0,
|
||||||
deviceId: 0,
|
deviceId: 0,
|
||||||
isCalculated: 1,
|
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
},
|
},
|
||||||
@ -468,6 +514,7 @@ export default {
|
|||||||
{ pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/, message: '以字母开头,只含字母、数字、下划线', trigger: 'blur' },
|
{ pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/, message: '以字母开头,只含字母、数字、下划线', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
datatype: [{ required: true, message: '请选择数据类型', trigger: 'change' }],
|
datatype: [{ required: true, message: '请选择数据类型', trigger: 'change' }],
|
||||||
|
isCalculated: [{ required: true, message: '请选择计算类型', trigger: 'change' }],
|
||||||
formula: [
|
formula: [
|
||||||
{ required: true, message: '请输入计算公式', trigger: 'none' },
|
{ required: true, message: '请输入计算公式', trigger: 'none' },
|
||||||
{ validator: this.validateFormula, trigger: 'none' },
|
{ validator: this.validateFormula, trigger: 'none' },
|
||||||
@ -515,12 +562,25 @@ export default {
|
|||||||
// 2. 校验变量格式:{标识符} 或 {slaveId:数字.标识符}(跨设备)
|
// 2. 校验变量格式:{标识符} 或 {slaveId:数字.标识符}(跨设备)
|
||||||
const variablePattern = /\{([^}]*)\}/g;
|
const variablePattern = /\{([^}]*)\}/g;
|
||||||
let match;
|
let match;
|
||||||
|
const variableNames = [];
|
||||||
const simpleVar = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
const simpleVar = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
||||||
const crossVar = /^slaveId:\d+\.[a-zA-Z][a-zA-Z0-9_]*$/;
|
const crossVar = /^slaveId:\d+\.[a-zA-Z][a-zA-Z0-9_]*$/;
|
||||||
while ((match = variablePattern.exec(formula)) !== null) {
|
while ((match = variablePattern.exec(formula)) !== null) {
|
||||||
const varName = match[1];
|
const varName = match[1];
|
||||||
|
variableNames.push(varName);
|
||||||
if (!simpleVar.test(varName) && !crossVar.test(varName)) {
|
if (!simpleVar.test(varName) && !crossVar.test(varName)) {
|
||||||
callback(new Error(`变量格式错误: {${varName}}。支持:{标识符} 或 {slaveId:从机号.标识符}(如 {slaveId:1.WaterH})`));
|
callback(new Error(`变量格式错误: {${varName}}。支持:{标识符}、{last} 或 {slaveId:从机号.标识符}(如 {slaveId:1.WaterH})`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.form.isCalculated === 2) {
|
||||||
|
if (!variableNames.includes('last')) {
|
||||||
|
callback(new Error('纵向累计公式必须包含 {last}'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const sourceVariables = variableNames.filter((name) => name !== 'last' && name !== this.form.identifier);
|
||||||
|
if (sourceVariables.length !== 1) {
|
||||||
|
callback(new Error('纵向累计公式必须且只能包含一个源物模型变量,如:{last}+{rain}'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -625,7 +685,15 @@ export default {
|
|||||||
placeholder: `{slaveId:${g.slaveId}.${m.identifier}}`,
|
placeholder: `{slaveId:${g.slaveId}.${m.identifier}}`,
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
this.productModels = [];
|
return listModel({
|
||||||
|
productId: this.device.productId,
|
||||||
|
deviceId: 0,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 200,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
this.productModels = (response.rows || []).filter((m) => m.isCalculated !== 1 && m.isCalculated !== 2);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loadingModels = false;
|
this.loadingModels = false;
|
||||||
@ -643,7 +711,7 @@ export default {
|
|||||||
pageSize: 200,
|
pageSize: 200,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.productModels = (response.rows || []).filter((m) => m.isCalculated !== 1);
|
this.productModels = (response.rows || []).filter((m) => m.isCalculated !== 1 && m.isCalculated !== 2);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loadingModels = false;
|
this.loadingModels = false;
|
||||||
@ -731,6 +799,11 @@ export default {
|
|||||||
: `{${identifierOrPlaceholder}}`;
|
: `{${identifierOrPlaceholder}}`;
|
||||||
this.insertAtCursor(text);
|
this.insertAtCursor(text);
|
||||||
},
|
},
|
||||||
|
/** 纵向累计直接生成标准公式 */
|
||||||
|
setVerticalFormula(identifier) {
|
||||||
|
this.form.formula = `{last}+{${identifier}}`;
|
||||||
|
this.focusFormulaInput();
|
||||||
|
},
|
||||||
/** 在光标位置插入内容(支持替换选中文本) */
|
/** 在光标位置插入内容(支持替换选中文本) */
|
||||||
insertAtCursor(text) {
|
insertAtCursor(text) {
|
||||||
const input = this.$refs.formulaInput;
|
const input = this.$refs.formulaInput;
|
||||||
@ -816,7 +889,6 @@ export default {
|
|||||||
|
|
||||||
// 格式化specs
|
// 格式化specs
|
||||||
this.form.specs = this.formatThingsSpecs();
|
this.form.specs = this.formatThingsSpecs();
|
||||||
this.form.isCalculated = 1;
|
|
||||||
this.form.isReadonly = 1;
|
this.form.isReadonly = 1;
|
||||||
this.form.type = 1;
|
this.form.type = 1;
|
||||||
// 去除公式中的空格
|
// 去除公式中的空格
|
||||||
|
|||||||
@ -96,7 +96,9 @@
|
|||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="批量推送">
|
<el-form-item label="批量推送">
|
||||||
<el-switch v-model="form.isBatchPush" :active-value="1" :inactive-value="0" active-text="是" inactive-text="否" />
|
<el-switch v-model="form.isBatchPush" :active-value="1" :inactive-value="0" active-text="是" inactive-text="否" />
|
||||||
<div style="font-size:12px;color:#909399;margin-top:4px">开启后一次请求推送全量子设备数据</div>
|
<el-tooltip content="开启后一次请求推送全量子设备数据" placement="top">
|
||||||
|
<i class="el-icon-question" style="color:#909399;margin-left:5px;cursor:help;"></i>
|
||||||
|
</el-tooltip>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|||||||
@ -140,7 +140,7 @@
|
|||||||
<el-input v-model="form.serialNumber" disabled />
|
<el-input v-model="form.serialNumber" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="设备名称">
|
<el-form-item label="设备名称">
|
||||||
<el-input v-model="form.deviceName" disabled />
|
<el-input v-model="form.deviceName" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="传感器类型" prop="sensorType">
|
<el-form-item label="传感器类型" prop="sensorType">
|
||||||
<el-select v-model="form.sensorType" placeholder="选择或输入设备类型" filterable allow-create default-first-option style="width:100%">
|
<el-select v-model="form.sensorType" placeholder="选择或输入设备类型" filterable allow-create default-first-option style="width:100%">
|
||||||
@ -165,13 +165,19 @@
|
|||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="推送模式" prop="pushMode">
|
<el-form-item label="推送模式" prop="pushMode">
|
||||||
<el-radio-group v-model="form.pushMode">
|
<el-radio-group v-model="form.pushMode">
|
||||||
<el-radio :label="1">实时推送</el-radio>
|
<el-radio :label="1">
|
||||||
<el-radio :label="2">定时推送</el-radio>
|
实时推送
|
||||||
|
<el-tooltip content="设备上报即刻推送" placement="top">
|
||||||
|
<i class="el-icon-question" style="color:#909399;margin-left:5px;cursor:help;"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio>
|
||||||
|
<el-radio :label="2">
|
||||||
|
定时推送
|
||||||
|
<el-tooltip content="按间隔推送最新数据" placement="top">
|
||||||
|
<i class="el-icon-question" style="color:#909399;margin-left:5px;cursor:help;"></i>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<div style="font-size:12px;color:#909399;margin-top:4px;line-height:1.5">
|
|
||||||
实时:设备上报即刻推送<br>
|
|
||||||
定时:按间隔推送最新数据
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12" v-if="form.pushMode === 2">
|
<el-col :span="12" v-if="form.pushMode === 2">
|
||||||
@ -589,7 +595,7 @@ export default {
|
|||||||
this.deviceOptions = [{ deviceId: device.deviceId, deviceName: device.deviceName, serialNumber: device.serialNumber }]
|
this.deviceOptions = [{ deviceId: device.deviceId, deviceName: device.deviceName, serialNumber: device.serialNumber }]
|
||||||
this.form.deviceId = device.deviceId
|
this.form.deviceId = device.deviceId
|
||||||
this.form.serialNumber = device.serialNumber
|
this.form.serialNumber = device.serialNumber
|
||||||
this.form.deviceName = device.deviceName || device.serialNumber
|
this.form.deviceName = this.form.deviceName || device.deviceName || device.serialNumber
|
||||||
this.loadThingsModel(device.deviceId)
|
this.loadThingsModel(device.deviceId)
|
||||||
this.loadSubDeviceOptions(device)
|
this.loadSubDeviceOptions(device)
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user