20260126:模型排序&高德地图
This commit is contained in:
parent
652a32b797
commit
b770da12e3
@ -102,7 +102,7 @@ spring:
|
||||
redis:
|
||||
host: localhost # 地址
|
||||
port: 6379 # 端口,默认为6379
|
||||
database: 3 # 数据库索引
|
||||
database: 0 # 数据库索引
|
||||
password: fastbee # 密码
|
||||
timeout: 10s # 连接超时时间
|
||||
lettuce:
|
||||
|
||||
@ -50,8 +50,11 @@ public class TCPBootStrap {
|
||||
.setPort(port)
|
||||
//设置报文最大长度 modbus-rtu
|
||||
.setMaxFrameLength(1024)
|
||||
// 此处就是ASCII和注册包粘包了,无需strip掉 “分隔符” 即可
|
||||
.setDelimiters(new Delimiter(new byte[]{0x7e}, false))
|
||||
/*
|
||||
* 此处就是ASCII和注册包粘包了,无需strip掉 “分隔符” 即可
|
||||
* 最原始.setDelimiters(new Delimiter(new byte[]{0x7e}, true))
|
||||
*/
|
||||
.setDelimiters(new Delimiter(new byte[]{0x0A}, true))
|
||||
.setDecoder(messageAdapter)
|
||||
.setEncoder(messageAdapter)
|
||||
.setHandlerMapping(handlerMapping)
|
||||
|
||||
@ -125,13 +125,12 @@ 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 + (keepDelimiterForNextFrame ? 0 : minDelimLength));
|
||||
buffer.skipBytes(minFrameLength + minDelimLength);
|
||||
|
||||
int tooLongFrameLength = this.tooLongFrameLength;
|
||||
this.tooLongFrameLength = 0;
|
||||
@ -143,16 +142,10 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
|
||||
/*小于最小长度帧处理*/
|
||||
if (minFrameLength > maxFrameLength) {
|
||||
//放弃读取帧
|
||||
buffer.skipBytes(minFrameLength + (keepDelimiterForNextFrame ? 0 : minDelimLength));
|
||||
buffer.skipBytes(minFrameLength + minDelimLength);
|
||||
fail(minFrameLength);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (keepDelimiterForNextFrame) {
|
||||
frame = buffer.readRetainedSlice(minFrameLength);
|
||||
return frame;
|
||||
}
|
||||
|
||||
/*是否需要跳过某字节*/
|
||||
if (minDelim.strip) {
|
||||
//忽略长度等于0的报文
|
||||
@ -273,9 +266,6 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Modbus RTU:为了避免把请求帧(常见固定8字节)误当响应帧(第3字节是byteCount)导致截断,
|
||||
// 这里采用"候选长度 + CRC校验"方式确定帧边界,兼容:请求/回显/响应/异常响应。
|
||||
|
||||
// 异常响应:地址(1) + 功能码(1|0x80) + 异常码(1) + CRC(2) = 5字节
|
||||
if ((functionCode & 0x80) != 0) {
|
||||
int len = 5;
|
||||
@ -309,8 +299,7 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
int byteCount = buffer.getByte(readerIndex + 2) & 0xFF;
|
||||
int respLen = 3 + byteCount + 2;
|
||||
log.debug("[{}] Modbus功能码{}: byteCount={}, 预期帧长={}, 可读={}",
|
||||
deviceId, String.format("%02X", functionCode), byteCount, respLen, readable);
|
||||
log.debug("[{}] Modbus功能码{}: byteCount={}, 预期帧长={}, 可读={}", deviceId, String.format("%02X", functionCode), byteCount, respLen, readable);
|
||||
if (respLen > maxFrameLength) {
|
||||
log.warn("[{}] Modbus帧长度{}超过最大限制{}", deviceId, respLen, maxFrameLength);
|
||||
return -1;
|
||||
@ -368,13 +357,7 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
|
||||
byte[] crc = CRC16Utils.getCrc16Byte(data); // [low, high]
|
||||
int crcLo = buffer.getByte(offset + len - 2) & 0xFF;
|
||||
int crcHi = buffer.getByte(offset + len - 1) & 0xFF;
|
||||
boolean valid = ((crc[0] & 0xFF) == crcLo) && ((crc[1] & 0xFF) == crcHi);
|
||||
if (!valid && log.isDebugEnabled()) {
|
||||
log.debug("[{}] CRC校验: 计算值={}{}, 实际值={}{}", deviceId,
|
||||
String.format("%02X", crc[0] & 0xFF), String.format("%02X", crc[1] & 0xFF),
|
||||
String.format("%02X", crcLo), String.format("%02X", crcHi));
|
||||
}
|
||||
return valid;
|
||||
return ((crc[0] & 0xFF) == crcLo) && ((crc[1] & 0xFF) == crcHi);
|
||||
}
|
||||
|
||||
private void fail(long frameLength) {
|
||||
|
||||
@ -114,4 +114,10 @@ public class ThingsModelValueItem {
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date ts;
|
||||
|
||||
/**
|
||||
* 创建时间(用于排序)
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
}
|
||||
|
||||
@ -599,6 +599,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
arrayObjectThingsModel.setIsReadonly(param.getIsReadonly());
|
||||
arrayObjectThingsModel.setDatatype(param.getDatatype());
|
||||
arrayObjectThingsModel.setModelOrder(thingsModel.getModelOrder());
|
||||
arrayObjectThingsModel.setCreateTime(thingsModel.getCreateTime());
|
||||
resultList.add(arrayObjectThingsModel);
|
||||
}
|
||||
}
|
||||
@ -617,6 +618,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
arrayThingsModel.setIsReadonly(thingsModel.getIsReadonly());
|
||||
arrayThingsModel.setDatatype(datatype);
|
||||
arrayThingsModel.setModelOrder(thingsModel.getModelOrder());
|
||||
arrayThingsModel.setCreateTime(thingsModel.getCreateTime());
|
||||
resultList.add(arrayThingsModel);
|
||||
}
|
||||
} else if ("object".equals(datatype.getType())) {
|
||||
@ -630,6 +632,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
newObjectThingsModel.setIsReadonly(objectThingsModel.getIsReadonly());
|
||||
newObjectThingsModel.setDatatype(objectThingsModel.getDatatype());
|
||||
newObjectThingsModel.setModelOrder(thingsModel.getModelOrder());
|
||||
newObjectThingsModel.setCreateTime(thingsModel.getCreateTime());
|
||||
resultList.add(newObjectThingsModel);
|
||||
}
|
||||
} else {
|
||||
@ -642,6 +645,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
otherThingsModel.setIsReadonly(thingsModel.getIsReadonly());
|
||||
otherThingsModel.setDatatype(datatype);
|
||||
otherThingsModel.setModelOrder(thingsModel.getModelOrder());
|
||||
otherThingsModel.setCreateTime(thingsModel.getCreateTime());
|
||||
resultList.add(otherThingsModel);
|
||||
}
|
||||
return resultList;
|
||||
@ -686,24 +690,31 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
if (null == productId) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 1、获取产品物模型
|
||||
// 排序器:按 modelOrder 降序,createTime 降序(与数据库 SQL 一致)
|
||||
Comparator<ThingsModelDTO> modelComparator = Comparator
|
||||
.comparing(ThingsModelDTO::getModelOrder, Comparator.reverseOrder())
|
||||
.thenComparing(ThingsModelDTO::getCreateTime, Comparator.nullsLast(Comparator.reverseOrder()));
|
||||
|
||||
// 1、获取产品物模型并排序
|
||||
Map<String, ThingsModelValueItem> map = itslCache.getCacheThMapByProductId(productId);
|
||||
List<ThingsModelDTO> thingsModelDTOList = new ArrayList<>();
|
||||
for (Map.Entry<String, ThingsModelValueItem> entry : map.entrySet()) {
|
||||
ThingsModel thingsModel = this.propertyDtoChangeThingsModel(entry.getValue());
|
||||
thingsModelDTOList.addAll(this.changeObjectOrArray(thingsModel));
|
||||
}
|
||||
thingsModelDTOList.sort(modelComparator);
|
||||
|
||||
// 2. 获取设备级计算物模型并合并
|
||||
if (StringUtils.isNotEmpty(serialNumber)) {
|
||||
Map<String, ThingsModelValueItem> deviceCalculatedMap = itslCache.getDeviceCalculatedModelMap(serialNumber);
|
||||
List<ThingsModelDTO> deviceModelList = new ArrayList<>();
|
||||
for (Map.Entry<String, ThingsModelValueItem> entry : deviceCalculatedMap.entrySet()) {
|
||||
ThingsModel thingsModel = this.propertyDtoChangeThingsModel(entry.getValue());
|
||||
thingsModelDTOList.addAll(this.changeObjectOrArray(thingsModel));
|
||||
deviceModelList.addAll(this.changeObjectOrArray(thingsModel));
|
||||
}
|
||||
deviceModelList.sort(modelComparator);
|
||||
thingsModelDTOList.addAll(deviceModelList);
|
||||
}
|
||||
|
||||
thingsModelDTOList.sort(Comparator.comparing(ThingsModelDTO::getModelOrder));
|
||||
if (StringUtils.isEmpty(serialNumber)) {
|
||||
return thingsModelDTOList;
|
||||
}
|
||||
@ -767,6 +778,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
thingsModel.setSpecs(JSON.toJSONString(valueItem.getDatatype()));
|
||||
thingsModel.setType(valueItem.getType());
|
||||
thingsModel.setModelId(valueItem.getModelId());
|
||||
thingsModel.setCreateTime(valueItem.getCreateTime());
|
||||
return thingsModel;
|
||||
}
|
||||
|
||||
|
||||
14
vue/package-lock.json
generated
14
vue/package-lock.json
generated
@ -5751,6 +5751,15 @@
|
||||
"zrender": "5.6.1"
|
||||
}
|
||||
},
|
||||
"echarts-extension-leaflet": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/echarts-extension-leaflet/-/echarts-extension-leaflet-1.2.2.tgz",
|
||||
"integrity": "sha512-EKZmIQDRqxnf6DtYWqujn2PRqHmEAq3yr0gsZAwmOT9rSy0Rwt9FlXKPjK3d+EVLqT6se6IQweyJTsqMg537Gg==",
|
||||
"requires": {
|
||||
"echarts": "^5.5.0",
|
||||
"leaflet": "^1.9.4"
|
||||
}
|
||||
},
|
||||
"echarts-liquidfill": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/echarts-liquidfill/-/echarts-liquidfill-3.1.0.tgz",
|
||||
@ -8974,6 +8983,11 @@
|
||||
"launch-editor": "^2.12.0"
|
||||
}
|
||||
},
|
||||
"leaflet": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmmirror.com/leaflet/-/leaflet-1.9.4.tgz",
|
||||
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="
|
||||
},
|
||||
"leven": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/leven/-/leven-2.1.0.tgz",
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
"codemirror": "^5.65.2",
|
||||
"core-js": "3.25.3",
|
||||
"echarts": "^5.4.0",
|
||||
"echarts-extension-leaflet": "^1.2.2",
|
||||
"echarts-liquidfill": "^3.0.6",
|
||||
"element-china-area-data": "^4.1.1",
|
||||
"element-ui": "2.15.10",
|
||||
|
||||
@ -1,28 +1,66 @@
|
||||
export function loadBMap() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (typeof BMap !== 'undefined') {
|
||||
resolve(BMap)
|
||||
return true
|
||||
}
|
||||
window.onBMapCallback = function() {
|
||||
resolve(BMap)
|
||||
}
|
||||
// 使用https协议需要添加一下meta标签
|
||||
var protocolStr = document.location.protocol;
|
||||
if(protocolStr == "https:")
|
||||
{
|
||||
let meta = document.createElement('meta')
|
||||
meta.httpEquiv = 'Content-Security-Policy'
|
||||
meta.content ='upgrade-insecure-requests'
|
||||
meta.onerror = reject
|
||||
document.head.appendChild(meta)
|
||||
}
|
||||
// 引入百度地图
|
||||
let script = document.createElement('script')
|
||||
script.type = 'text/javascript'
|
||||
script.src =
|
||||
'http://api.map.baidu.com/api?v=2.0&ak='+ process.env.VUE_APP_BAI_DU_AK +'&s=1&__ec_v__=20190126&callback=onBMapCallback'
|
||||
script.onerror = reject
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
// 使用OpenStreetMap和Leaflet替代百度地图
|
||||
|
||||
export function loadMap() {
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
// eslint-disable-next-line no-undef
|
||||
if (typeof L !== 'undefined') {
|
||||
// eslint-disable-next-line no-undef
|
||||
resolve(L);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 加载Leaflet CSS
|
||||
if (!document.querySelector('link[href*="leaflet.css"]')) {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
|
||||
link.integrity = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=';
|
||||
link.crossOrigin = '';
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
// 加载Leaflet JS
|
||||
const script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
|
||||
script.integrity = 'sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=';
|
||||
script.crossOrigin = '';
|
||||
script.onload = function() {
|
||||
// eslint-disable-next-line no-undef
|
||||
resolve(L);
|
||||
};
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// export function loadBMap() {
|
||||
// return new Promise(function(resolve, reject) {
|
||||
// if (typeof BMap !== 'undefined') {
|
||||
// resolve(BMap)
|
||||
// return true
|
||||
// }
|
||||
// window.onBMapCallback = function() {
|
||||
// resolve(BMap)
|
||||
// }
|
||||
// // 使用https协议需要添加一下meta标签
|
||||
// var protocolStr = document.location.protocol;
|
||||
// if(protocolStr == "https:")
|
||||
// {
|
||||
// let meta = document.createElement('meta')
|
||||
// meta.httpEquiv = 'Content-Security-Policy'
|
||||
// meta.content ='upgrade-insecure-requests'
|
||||
// meta.onerror = reject
|
||||
// document.head.appendChild(meta)
|
||||
// }
|
||||
// // 引入百度地图
|
||||
// let script = document.createElement('script')
|
||||
// script.type = 'text/javascript'
|
||||
// script.src =
|
||||
// 'http://api.map.baidu.com/api?v=2.0&ak='+ process.env.VUE_APP_BAI_DU_AK +'&s=1&__ec_v__=20190126&callback=onBMapCallback'
|
||||
// script.onerror = reject
|
||||
// document.head.appendChild(script)
|
||||
// })
|
||||
// }
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
<script>
|
||||
import {
|
||||
loadBMap
|
||||
loadMap
|
||||
} from '@/utils/map.js'
|
||||
//安装的是echarts完整包,里面包含百度地图扩展,路径为 echarts/extension/bmap/bmap.js,将其引入
|
||||
//ECharts的百度地图扩展,可以在百度地图上展现点图,线图,热力图等可视化
|
||||
@ -65,7 +65,7 @@ export default {
|
||||
},
|
||||
/**加载地图*/
|
||||
loadMap() {
|
||||
loadBMap().then(() => {
|
||||
loadMap().then(() => {
|
||||
this.getmap();
|
||||
});
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -31,19 +31,25 @@
|
||||
</div>
|
||||
<div class="item-content">
|
||||
<div class="content-value">
|
||||
<!-- 根据format选择显示HEX或ASCII格式 -->
|
||||
<div v-if="format === 'Hex' && item.value" class="data-format-item">
|
||||
<span class="format-label">HEX:</span>
|
||||
<span class="hex-value">{{ item.value }}</span>
|
||||
</div>
|
||||
<div v-if="format === 'ASCII' && item.valueAscii" class="data-format-item ascii-item">
|
||||
<span class="format-label">ASCII:</span>
|
||||
<pre class="ascii-value">{{ item.valueAscii }}</pre>
|
||||
</div>
|
||||
<div v-if="format === 'JSON'" class="data-format-item">
|
||||
<!-- 自动识别并展示所有可用格式 -->
|
||||
<!-- JSON格式优先展示(如果是有效JSON) -->
|
||||
<div v-if="isValidJson(item.valueAscii || item.value)" class="data-format-item json-item">
|
||||
<span class="format-label">JSON:</span>
|
||||
<pre class="json-value">{{ formatJsonValue(item.value, item.valueAscii) }}</pre>
|
||||
</div>
|
||||
<!-- 非JSON时展示HEX和ASCII -->
|
||||
<template v-else>
|
||||
<!-- HEX格式 -->
|
||||
<div v-if="item.value" class="data-format-item">
|
||||
<span class="format-label">HEX:</span>
|
||||
<span class="hex-value">{{ item.value }}</span>
|
||||
</div>
|
||||
<!-- ASCII格式(如果有可读内容) -->
|
||||
<div v-if="item.valueAscii && hasReadableAscii(item.valueAscii)" class="data-format-item ascii-item">
|
||||
<span class="format-label">ASCII:</span>
|
||||
<pre class="ascii-value">{{ item.valueAscii }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis-data" v-if="item.type != 0 && item.analysis">
|
||||
@ -564,6 +570,24 @@ export default {
|
||||
}
|
||||
// 如果都不是JSON,返回提示信息
|
||||
return '数据不是有效的JSON格式';
|
||||
},
|
||||
/**判断是否为有效的JSON */
|
||||
isValidJson(value) {
|
||||
if (!value) return false;
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
// 确保是对象或数组,而不是简单值
|
||||
return typeof parsed === 'object' && parsed !== null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
/**判断ASCII是否包含可读内容 */
|
||||
hasReadableAscii(asciiValue) {
|
||||
if (!asciiValue) return false;
|
||||
// 检查是否有可打印字符(排除纯控制字符)
|
||||
const printableRegex = /[\x20-\x7E]/;
|
||||
return printableRegex.test(asciiValue) && asciiValue.trim().length > 0;
|
||||
},
|
||||
/**打开编辑框 */
|
||||
openEdit() {
|
||||
@ -990,6 +1014,11 @@ $right-btn-color: #1890ff;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// 高度 = 头部36px + 内容700px + 底部64px
|
||||
height: 800px;
|
||||
|
||||
.right-head {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
@ -997,19 +1026,28 @@ $right-btn-color: #1890ff;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// 覆盖通用 .content 样式
|
||||
.content.beautify-scroll-def {
|
||||
flex: 1;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.quick-item {
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
margin: 0 auto 12px auto;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
margin-top: 12px;
|
||||
border: 1px solid $right-btn-color;
|
||||
color: $right-btn-color;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 12px;
|
||||
&:first-child {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user