20251213:数据中心-历史记录-设备-导出历史数据
This commit is contained in:
parent
d965eab5d6
commit
d0526b4e46
@ -158,11 +158,23 @@ public class DeviceDebugLogAspect {
|
|||||||
if (msg instanceof byte[]) {
|
if (msg instanceof byte[]) {
|
||||||
sourceData = ByteBufUtil.hexDump((byte[]) msg);
|
sourceData = ByteBufUtil.hexDump((byte[]) msg);
|
||||||
} else {
|
} else {
|
||||||
sourceData = msg.toString();
|
String msgStr = msg.toString();
|
||||||
|
// 如果dataType是ascii,需要将ASCII字符串转换为HEX格式用于显示
|
||||||
|
if ("ascii".equalsIgnoreCase(deviceMessage.getDataType())) {
|
||||||
|
// ASCII字符串转HEX
|
||||||
|
sourceData = asciiToHex(msgStr);
|
||||||
|
} else {
|
||||||
|
// 其他情况直接使用原字符串(可能是HEX格式)
|
||||||
|
sourceData = msgStr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.warn("处理原始数据失败: {}", ex.getMessage());
|
log.warn("处理原始数据失败: {}", ex.getMessage());
|
||||||
|
// 如果转换失败,至少保存原始字符串
|
||||||
|
if (deviceMessage.getMessage() != null) {
|
||||||
|
sourceData = deviceMessage.getMessage().toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String errorMsg = e.getMessage();
|
String errorMsg = e.getMessage();
|
||||||
@ -395,6 +407,37 @@ public class DeviceDebugLogAspect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将ASCII字符串转换为HEX字符串
|
||||||
|
* @param asciiStr ASCII字符串
|
||||||
|
* @return HEX字符串(大写,每两个字符一组,用空格分隔)
|
||||||
|
*/
|
||||||
|
private String asciiToHex(String asciiStr) {
|
||||||
|
if (StringUtils.isEmpty(asciiStr)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
StringBuilder hex = new StringBuilder();
|
||||||
|
for (int i = 0; i < asciiStr.length(); i++) {
|
||||||
|
char c = asciiStr.charAt(i);
|
||||||
|
String hexByte = Integer.toHexString(c & 0xFF).toUpperCase();
|
||||||
|
if (hexByte.length() == 1) {
|
||||||
|
hex.append("0");
|
||||||
|
}
|
||||||
|
hex.append(hexByte);
|
||||||
|
// 每两个字节之间加一个空格,便于阅读
|
||||||
|
if (i < asciiStr.length() - 1) {
|
||||||
|
hex.append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hex.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("ASCII转HEX失败: {}, ASCII字符串: {}", e.getMessage(),
|
||||||
|
StringUtils.isNotEmpty(asciiStr) ? StringUtils.substring(asciiStr, 0, 50) : "");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将16进制字符串转换为ASCII字符串
|
* 将16进制字符串转换为ASCII字符串
|
||||||
* @param hexStr 16进制字符串(可能包含空格)
|
* @param hexStr 16进制字符串(可能包含空格)
|
||||||
|
|||||||
@ -159,8 +159,9 @@ public class DataCenterController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取变量名称映射(identifier -> modelName)
|
// 获取变量名称映射(identifier -> modelName)和数据类型映射(identifier -> datatype)
|
||||||
Map<String, String> identifierNameMap = new LinkedHashMap<>();
|
Map<String, String> identifierNameMap = new LinkedHashMap<>();
|
||||||
|
Map<String, String> identifierTypeMap = new LinkedHashMap<>(); // 存储identifier到数据类型的映射
|
||||||
List<String> headerList = new ArrayList<>();
|
List<String> headerList = new ArrayList<>();
|
||||||
|
|
||||||
if (deviceHistoryParam.getIdentifierList() != null && !deviceHistoryParam.getIdentifierList().isEmpty()) {
|
if (deviceHistoryParam.getIdentifierList() != null && !deviceHistoryParam.getIdentifierList().isEmpty()) {
|
||||||
@ -169,7 +170,7 @@ public class DataCenterController {
|
|||||||
.map(DeviceHistoryParam.IdentifierVO::getIdentifier)
|
.map(DeviceHistoryParam.IdentifierVO::getIdentifier)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
// 查询物模型获取中文名称
|
// 查询物模型获取中文名称和数据类型
|
||||||
if (deviceHistoryParam.getDeviceId() != null) {
|
if (deviceHistoryParam.getDeviceId() != null) {
|
||||||
List<ThingsModelDTO> thingsModelList = deviceService.listThingsModel(deviceHistoryParam.getDeviceId());
|
List<ThingsModelDTO> thingsModelList = deviceService.listThingsModel(deviceHistoryParam.getDeviceId());
|
||||||
Map<String, ThingsModelDTO> thingsModelMap = thingsModelList.stream()
|
Map<String, ThingsModelDTO> thingsModelMap = thingsModelList.stream()
|
||||||
@ -180,6 +181,12 @@ public class DataCenterController {
|
|||||||
String headerName = thingsModel != null && StringUtils.isNotEmpty(thingsModel.getModelName())
|
String headerName = thingsModel != null && StringUtils.isNotEmpty(thingsModel.getModelName())
|
||||||
? thingsModel.getModelName() : identifier;
|
? thingsModel.getModelName() : identifier;
|
||||||
identifierNameMap.put(identifier, headerName);
|
identifierNameMap.put(identifier, headerName);
|
||||||
|
// 获取数据类型
|
||||||
|
String dataType = "string"; // 默认字符串类型
|
||||||
|
if (thingsModel != null && thingsModel.getDatatype() != null && StringUtils.isNotEmpty(thingsModel.getDatatype().getType())) {
|
||||||
|
dataType = thingsModel.getDatatype().getType();
|
||||||
|
}
|
||||||
|
identifierTypeMap.put(identifier, dataType);
|
||||||
headerList.add(headerName);
|
headerList.add(headerName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -187,6 +194,7 @@ public class DataCenterController {
|
|||||||
headerList.addAll(identifierList);
|
headerList.addAll(identifierList);
|
||||||
for (String identifier : identifierList) {
|
for (String identifier : identifierList) {
|
||||||
identifierNameMap.put(identifier, identifier);
|
identifierNameMap.put(identifier, identifier);
|
||||||
|
identifierTypeMap.put(identifier, "string"); // 默认字符串类型
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!dataList.isEmpty()) {
|
} else if (!dataList.isEmpty()) {
|
||||||
@ -204,7 +212,7 @@ public class DataCenterController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询物模型获取中文名称
|
// 查询物模型获取中文名称和数据类型
|
||||||
if (deviceHistoryParam.getDeviceId() != null) {
|
if (deviceHistoryParam.getDeviceId() != null) {
|
||||||
List<ThingsModelDTO> thingsModelList = deviceService.listThingsModel(deviceHistoryParam.getDeviceId());
|
List<ThingsModelDTO> thingsModelList = deviceService.listThingsModel(deviceHistoryParam.getDeviceId());
|
||||||
Map<String, ThingsModelDTO> thingsModelMap = thingsModelList.stream()
|
Map<String, ThingsModelDTO> thingsModelMap = thingsModelList.stream()
|
||||||
@ -215,6 +223,12 @@ public class DataCenterController {
|
|||||||
String headerName = thingsModel != null && StringUtils.isNotEmpty(thingsModel.getModelName())
|
String headerName = thingsModel != null && StringUtils.isNotEmpty(thingsModel.getModelName())
|
||||||
? thingsModel.getModelName() : identifier;
|
? thingsModel.getModelName() : identifier;
|
||||||
identifierNameMap.put(identifier, headerName);
|
identifierNameMap.put(identifier, headerName);
|
||||||
|
// 获取数据类型
|
||||||
|
String dataType = "string"; // 默认字符串类型
|
||||||
|
if (thingsModel != null && thingsModel.getDatatype() != null && StringUtils.isNotEmpty(thingsModel.getDatatype().getType())) {
|
||||||
|
dataType = thingsModel.getDatatype().getType();
|
||||||
|
}
|
||||||
|
identifierTypeMap.put(identifier, dataType);
|
||||||
headerList.add(headerName);
|
headerList.add(headerName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -222,6 +236,7 @@ public class DataCenterController {
|
|||||||
headerList.addAll(identifierSet);
|
headerList.addAll(identifierSet);
|
||||||
for (String identifier : identifierSet) {
|
for (String identifier : identifierSet) {
|
||||||
identifierNameMap.put(identifier, identifier);
|
identifierNameMap.put(identifier, identifier);
|
||||||
|
identifierTypeMap.put(identifier, "string"); // 默认字符串类型
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,11 +261,19 @@ public class DataCenterController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按照identifierNameMap的顺序添加数据
|
// 按照identifierNameMap的顺序添加数据,并根据数据类型转换
|
||||||
for (Map.Entry<String, String> entry : identifierNameMap.entrySet()) {
|
for (Map.Entry<String, String> entry : identifierNameMap.entrySet()) {
|
||||||
String identifier = entry.getKey();
|
String identifier = entry.getKey();
|
||||||
Object value = valueMap.get(identifier);
|
Object value = valueMap.get(identifier);
|
||||||
rowData.add(value != null ? value : "");
|
|
||||||
|
// 根据数据类型转换值
|
||||||
|
if (value != null) {
|
||||||
|
String dataType = identifierTypeMap.getOrDefault(identifier, "string");
|
||||||
|
Object convertedValue = convertValueByType(value, dataType);
|
||||||
|
rowData.add(convertedValue);
|
||||||
|
} else {
|
||||||
|
rowData.add("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exportDataList.add(rowData);
|
exportDataList.add(rowData);
|
||||||
@ -280,4 +303,87 @@ public class DataCenterController {
|
|||||||
excelWriter.finish();
|
excelWriter.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据数据类型转换值
|
||||||
|
* @param value 原始值
|
||||||
|
* @param dataType 数据类型(integer、decimal、string、bool、enum等)
|
||||||
|
* @return 转换后的值
|
||||||
|
*/
|
||||||
|
private Object convertValueByType(Object value, String dataType) {
|
||||||
|
if (value == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (dataType) {
|
||||||
|
case "integer":
|
||||||
|
// 整数类型:转换为Integer
|
||||||
|
if (value instanceof Number) {
|
||||||
|
return ((Number) value).intValue();
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
String str = ((String) value).trim();
|
||||||
|
if (str.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// 尝试解析为整数
|
||||||
|
try {
|
||||||
|
// 如果是小数,先转double再转int
|
||||||
|
if (str.contains(".")) {
|
||||||
|
return (int) Double.parseDouble(str);
|
||||||
|
}
|
||||||
|
return Integer.parseInt(str);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 如果无法解析,返回原值
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
|
||||||
|
case "decimal":
|
||||||
|
// 小数类型:转换为Double
|
||||||
|
if (value instanceof Number) {
|
||||||
|
return ((Number) value).doubleValue();
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
String str = ((String) value).trim();
|
||||||
|
if (str.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(str);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 如果无法解析,返回原值
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
|
||||||
|
case "bool":
|
||||||
|
// 布尔类型:转换为Boolean或字符串
|
||||||
|
if (value instanceof Boolean) {
|
||||||
|
return value;
|
||||||
|
} else if (value instanceof Number) {
|
||||||
|
return ((Number) value).intValue() != 0;
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
String str = ((String) value).trim().toLowerCase();
|
||||||
|
if ("true".equals(str) || "1".equals(str) || "yes".equals(str)) {
|
||||||
|
return true;
|
||||||
|
} else if ("false".equals(str) || "0".equals(str) || "no".equals(str)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
|
||||||
|
case "string":
|
||||||
|
case "enum":
|
||||||
|
default:
|
||||||
|
// 字符串类型和其他类型:保持原样或转为字符串
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 转换失败时返回原值
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
# 页面标题
|
# 页面标题
|
||||||
VUE_APP_TITLE = FastBee物联网平台
|
VUE_APP_TITLE = 岩安测控数据平台
|
||||||
|
|
||||||
# 开发服务器端口
|
# 开发服务器端口
|
||||||
port = 80
|
port = 80
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# 页面标题
|
# 页面标题
|
||||||
VUE_APP_TITLE = FastBee物联网系统
|
VUE_APP_TITLE = 岩安测控数据平台
|
||||||
|
|
||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
ENV = 'production'
|
ENV = 'production'
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# 页面标题
|
# 页面标题
|
||||||
VUE_APP_TITLE = FastBee物联网系统
|
VUE_APP_TITLE = 岩安测控数据平台
|
||||||
|
|
||||||
NODE_ENV = production
|
NODE_ENV = production
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "fastbee",
|
"name": "fastbee",
|
||||||
"version": "3.8.5",
|
"version": "3.8.5",
|
||||||
"description": "FastBee物联网平台",
|
"description": "岩安测控数据平台",
|
||||||
"author": "kerwincui",
|
"author": "kerwincui",
|
||||||
"license": "AGPL3.0",
|
"license": "AGPL3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -112,7 +112,7 @@
|
|||||||
"home.disk": "System disk usage rate",
|
"home.disk": "System disk usage rate",
|
||||||
"home.diskStatus": "Disk status G",
|
"home.diskStatus": "Disk status G",
|
||||||
"home.available": "available",
|
"home.available": "available",
|
||||||
"views.index.394840-0": "FastBee IoT Platform",
|
"views.index.394840-0": "YAN AN CE KONG IoT Platform",
|
||||||
"views.index.394840-1": "The open source version adopts the AGPL-3.0 license agreement, and commercial use requires authorization",
|
"views.index.394840-1": "The open source version adopts the AGPL-3.0 license agreement, and commercial use requires authorization",
|
||||||
"views.index.394840-2": "Open source version:",
|
"views.index.394840-2": "Open source version:",
|
||||||
"views.index.394840-3": "Can be used for personal learning and use, non-commercial purposes",
|
"views.index.394840-3": "Can be used for personal learning and use, non-commercial purposes",
|
||||||
|
|||||||
@ -36,11 +36,11 @@
|
|||||||
"login.989807-34": "微信登录",
|
"login.989807-34": "微信登录",
|
||||||
"login.989807-35": "注册账号",
|
"login.989807-35": "注册账号",
|
||||||
"login.989807-36": "注册绑定账号",
|
"login.989807-36": "注册绑定账号",
|
||||||
"login.989807-37": "蜂信物联",
|
"login.989807-37": "岩安测控数据平台",
|
||||||
"login.989807-38": "FastBee开源物联网平台,简单易用,更适合中小企业和个人学习使用的物联网平台。",
|
"login.989807-38": "岩安测控数据平台,简单易用,更适合中小企业和个人学习使用的物联网平台。",
|
||||||
"login.989807-39": "FastBee系统采用国内流行度最高的技术栈,帮助更多的企业实现设备联网控制和监测。",
|
"login.989807-39": "岩安测控数据平台采用国内流行度最高的技术栈,帮助更多的企业实现设备联网控制和监测。",
|
||||||
"register.974236-0": "账号注册",
|
"register.974236-0": "账号注册",
|
||||||
"register.974236-1": "蜂信物联",
|
"register.974236-1": "岩安测控",
|
||||||
"register.974236-2": "手机号码",
|
"register.974236-2": "手机号码",
|
||||||
"register.974236-3": "确认密码",
|
"register.974236-3": "确认密码",
|
||||||
"register.974236-4": "注 册",
|
"register.974236-4": "注 册",
|
||||||
@ -50,7 +50,7 @@
|
|||||||
"register.974236-8": "查看文档",
|
"register.974236-8": "查看文档",
|
||||||
"register.974236-9": "账号登录",
|
"register.974236-9": "账号登录",
|
||||||
"register.974236-10": "绑定中...",
|
"register.974236-10": "绑定中...",
|
||||||
"register.974236-11": "FastBee开源物联网平台,简单易用,更适合中小企业和个人学习使用的物联网平台。",
|
"register.974236-11": "岩安测控数据平台,简单易用,更适合中小企业和个人学习使用的物联网平台。",
|
||||||
"register.974236-12": "两次输入的密码不一致",
|
"register.974236-12": "两次输入的密码不一致",
|
||||||
"register.974236-13": "用户账号长度必须介于 2 和 20 之间",
|
"register.974236-13": "用户账号长度必须介于 2 和 20 之间",
|
||||||
"register.974236-14": "请输入您的手机号码",
|
"register.974236-14": "请输入您的手机号码",
|
||||||
@ -112,7 +112,7 @@
|
|||||||
"home.disk": "系统盘使用率",
|
"home.disk": "系统盘使用率",
|
||||||
"home.diskStatus": "磁盘状态 G",
|
"home.diskStatus": "磁盘状态 G",
|
||||||
"home.available": "可用",
|
"home.available": "可用",
|
||||||
"views.index.394840-0": "FastBee物联网平台",
|
"views.index.394840-0": "岩安测控数据平台",
|
||||||
"views.index.394840-1": "开源版本采用AGPL-3.0许可协议,商用需要获得授权",
|
"views.index.394840-1": "开源版本采用AGPL-3.0许可协议,商用需要获得授权",
|
||||||
"views.index.394840-2": "开源版本:",
|
"views.index.394840-2": "开源版本:",
|
||||||
"views.index.394840-3": "可用于个人学习和使用,非商业用途",
|
"views.index.394840-3": "可用于个人学习和使用,非商业用途",
|
||||||
@ -127,7 +127,7 @@
|
|||||||
"views.index.394840-12": "系统源码",
|
"views.index.394840-12": "系统源码",
|
||||||
"views.index.394840-13": "Gitee源码",
|
"views.index.394840-13": "Gitee源码",
|
||||||
"views.index.394840-14": "Github源码",
|
"views.index.394840-14": "Github源码",
|
||||||
"views.index.394840-15": "蜂信物联",
|
"views.index.394840-15": "岩安测控",
|
||||||
"views.index.394840-16": "登录用户数量",
|
"views.index.394840-16": "登录用户数量",
|
||||||
"views.index.394840-17": "项目文档",
|
"views.index.394840-17": "项目文档",
|
||||||
"tagsView.refresh": "刷新",
|
"tagsView.refresh": "刷新",
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
title: 'FastBee',
|
title: '岩安测控',
|
||||||
logo: logoImg,
|
logo: logoImg,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
<div class="guang"></div>
|
<div class="guang"></div>
|
||||||
<div class="d-flex jc-center">
|
<div class="d-flex jc-center">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="title-text">FastBee物联网平台</span>
|
<span class="title-text">岩安测控数据平台</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="timers ">
|
<div class="timers ">
|
||||||
|
|||||||
@ -583,8 +583,9 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 有效的Excel文件,直接下载
|
// 有效的Excel文件,直接下载 - 使用设备编号作为文件名
|
||||||
const fileName = `设备历史数据_${moment().format('YYYYMMDDHHmmss')}.xlsx`;
|
const deviceSerialNumber = devices.serialNumber || '设备数据';
|
||||||
|
const fileName = `${deviceSerialNumber}_${moment().format('YYYYMMDDHHmmss')}.xlsx`;
|
||||||
saveAs(res, fileName);
|
saveAs(res, fileName);
|
||||||
this.$message.success('导出成功');
|
this.$message.success('导出成功');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -882,11 +882,11 @@ export default {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
option = {
|
option = {
|
||||||
title: {
|
title: {
|
||||||
text: this.$t('home.onlineDevice') + this.deviceList.filter((x) => x.status == 3).length + ')',
|
text: this.$t('home.onlineDevice') + this.deviceList.filter((x) => x.status == 3).length + ')',
|
||||||
subtext: 'Fastbee open source iot platform',
|
subtext: '岩安测控数据平台',
|
||||||
sublink: 'https://iot.fastbee.cn',
|
sublink: 'https://iot.fastbee.cn', // todo 替换为延安测控的官网
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: '#333',
|
color: '#333',
|
||||||
|
|||||||
@ -5,13 +5,13 @@
|
|||||||
<div class="head">
|
<div class="head">
|
||||||
<span style="color: #909399">{{ $t('device.instruction-parsing.830424-0') }}</span>
|
<span style="color: #909399">{{ $t('device.instruction-parsing.830424-0') }}</span>
|
||||||
<span>{{ device.serialNumber }}</span>
|
<span>{{ device.serialNumber }}</span>
|
||||||
<el-dropdown style="margin-left: auto">
|
<el-dropdown style="margin-left: auto" @command="handleFormatChange">
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
{{ format }}
|
{{ format }}
|
||||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item v-for="(item, index) in formatList" :key="index">{{ item }}</el-dropdown-item>
|
<el-dropdown-item v-for="(item, index) in formatList" :key="index" :command="item">{{ item }}</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
@ -31,16 +31,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="item-content">
|
<div class="item-content">
|
||||||
<div class="content-value">
|
<div class="content-value">
|
||||||
<!-- 显示HEX格式 -->
|
<!-- 根据format选择显示HEX或ASCII格式 -->
|
||||||
<div v-if="item.value" class="data-format-item">
|
<div v-if="format === 'Hex' && item.value" class="data-format-item">
|
||||||
<span class="format-label">HEX:</span>
|
<span class="format-label">HEX:</span>
|
||||||
<span class="hex-value">{{ item.value }}</span>
|
<span class="hex-value">{{ item.value }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 显示ASCII格式 -->
|
<div v-if="format === 'ASCII' && item.valueAscii" class="data-format-item ascii-item">
|
||||||
<div v-if="item.valueAscii" class="data-format-item ascii-item">
|
|
||||||
<span class="format-label">ASCII:</span>
|
<span class="format-label">ASCII:</span>
|
||||||
<pre class="ascii-value">{{ item.valueAscii }}</pre>
|
<pre class="ascii-value">{{ item.valueAscii }}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="format === 'JSON'" class="data-format-item">
|
||||||
|
<span class="format-label">JSON:</span>
|
||||||
|
<pre class="json-value">{{ formatJsonValue(item.value, item.valueAscii) }}</pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="analysis-data" v-if="item.type != 0 && item.analysis">
|
<div class="analysis-data" v-if="item.type != 0 && item.analysis">
|
||||||
@ -63,10 +66,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom">
|
<div class="bottom">
|
||||||
<el-input :placeholder="$t('device.instruction-parsing.830424-10')" v-model="sendVal" class="input-with-select" size="medium">
|
<el-input :placeholder="$t('device.instruction-parsing.830424-10')" v-model="sendVal" class="input-with-select" size="medium" @keyup.enter.native="sendMessage">
|
||||||
<el-select v-model="dataType" slot="prepend" :placeholder="$t('device.instruction-parsing.830424-11')">
|
<el-select v-model="dataType" slot="prepend" :placeholder="$t('device.instruction-parsing.830424-11')">
|
||||||
<el-option label="hex" value="hex"></el-option>
|
<el-option label="hex" value="hex"></el-option>
|
||||||
<el-option label="json" value="json"></el-option>
|
<el-option label="json" value="json"></el-option>
|
||||||
|
<el-option label="ascii" value="ascii"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-button round type="primary" class="send-btn" size="mini" @click.stop="sendMessage">{{ $t('device.instruction-parsing.830424-14') }}</el-button>
|
<el-button round type="primary" class="send-btn" size="mini" @click.stop="sendMessage">{{ $t('device.instruction-parsing.830424-14') }}</el-button>
|
||||||
@ -411,7 +415,7 @@ export default {
|
|||||||
//选中的格式
|
//选中的格式
|
||||||
format: 'Hex',
|
format: 'Hex',
|
||||||
//格式列表
|
//格式列表
|
||||||
formatList: ['Hex', 'JSON', 'Plaintext'],
|
formatList: ['Hex', 'JSON', 'ASCII'],
|
||||||
//数据列表
|
//数据列表
|
||||||
logList: [],
|
logList: [],
|
||||||
//发送的命令
|
//发送的命令
|
||||||
@ -499,6 +503,68 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/**处理updateData事件 */
|
||||||
|
handleUpdateData(params) {
|
||||||
|
const { serialNumber, productId, data } = params;
|
||||||
|
if (serialNumber == this.device.serialNumber) {
|
||||||
|
this.logList.push({
|
||||||
|
//0是下发,1是回复,2是上报
|
||||||
|
type: 2,
|
||||||
|
time: formatDate(new Date()),
|
||||||
|
value: data.sources, // HEX格式
|
||||||
|
valueAscii: data.sourcesAscii || '', // ASCII格式
|
||||||
|
loading: false,
|
||||||
|
analysis: false,
|
||||||
|
//解析的数据数组
|
||||||
|
analysisList: data.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**处理格式切换 */
|
||||||
|
handleFormatChange(command) {
|
||||||
|
this.format = command;
|
||||||
|
},
|
||||||
|
/**格式化JSON值 */
|
||||||
|
formatJsonValue(hexValue, asciiValue) {
|
||||||
|
// 优先尝试从ASCII值解析JSON
|
||||||
|
if (asciiValue) {
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(asciiValue);
|
||||||
|
return JSON.stringify(json, null, 2);
|
||||||
|
} catch (e) {
|
||||||
|
// ASCII不是JSON,继续尝试HEX
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 尝试从HEX值解析JSON
|
||||||
|
if (hexValue) {
|
||||||
|
try {
|
||||||
|
// 先尝试直接解析
|
||||||
|
const json = JSON.parse(hexValue);
|
||||||
|
return JSON.stringify(json, null, 2);
|
||||||
|
} catch (e) {
|
||||||
|
// 如果不是JSON字符串,尝试将HEX转为字符串再解析
|
||||||
|
try {
|
||||||
|
const hexStr = hexValue.replace(/\s+/g, '');
|
||||||
|
let jsonStr = '';
|
||||||
|
for (let i = 0; i < hexStr.length; i += 2) {
|
||||||
|
const hexByte = hexStr.substr(i, 2);
|
||||||
|
const charCode = parseInt(hexByte, 16);
|
||||||
|
if (charCode >= 32 && charCode <= 126) {
|
||||||
|
jsonStr += String.fromCharCode(charCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (jsonStr) {
|
||||||
|
const json = JSON.parse(jsonStr);
|
||||||
|
return JSON.stringify(json, null, 2);
|
||||||
|
}
|
||||||
|
} catch (e2) {
|
||||||
|
// 转换失败
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果都不是JSON,返回提示信息
|
||||||
|
return '数据不是有效的JSON格式';
|
||||||
|
},
|
||||||
/**打开编辑框 */
|
/**打开编辑框 */
|
||||||
openEdit() {
|
openEdit() {
|
||||||
this.resetCreateForm();
|
this.resetCreateForm();
|
||||||
@ -847,24 +913,14 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.resetCreateForm();
|
this.resetCreateForm();
|
||||||
this.$busEvent.$on('updateData', (params) => {
|
// 先移除旧的监听器,避免重复绑定
|
||||||
const { serialNumber, productId, data } = params;
|
this.$busEvent.$off('updateData', this.handleUpdateData);
|
||||||
if (serialNumber == this.device.serialNumber) {
|
// 绑定新的监听器
|
||||||
this.logList.push({
|
this.$busEvent.$on('updateData', this.handleUpdateData);
|
||||||
//0是下发,1是回复,2是上报
|
|
||||||
type: 2,
|
|
||||||
time: formatDate(new Date()),
|
|
||||||
value: data.sources, // HEX格式
|
|
||||||
valueAscii: data.sourcesAscii || '', // ASCII格式
|
|
||||||
loading: false,
|
|
||||||
analysis: false,
|
|
||||||
//解析的数据数组
|
|
||||||
analysisList: data.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
// 移除所有事件监听器
|
||||||
|
this.$busEvent.$off('updateData', this.handleUpdateData);
|
||||||
this.$busEvent.$off('updateMqttMessage');
|
this.$busEvent.$off('updateMqttMessage');
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -1076,7 +1132,8 @@ $right-btn-color: #1890ff;
|
|||||||
font-family: 'Courier New', monospace;
|
font-family: 'Courier New', monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ascii-value {
|
.ascii-value,
|
||||||
|
.json-value {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
background-color: #f5f7fa;
|
background-color: #f5f7fa;
|
||||||
|
|||||||
@ -643,7 +643,7 @@ export default {
|
|||||||
// 对象类型
|
// 对象类型
|
||||||
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.params.length; n++) {
|
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.params.length; n++) {
|
||||||
if (this.deviceInfo.thingsModels[k].datatype.params[n].id == data[j].id) {
|
if (this.deviceInfo.thingsModels[k].datatype.params[n].id == data[j].id) {
|
||||||
this.deviceInfo.thingsModels[k].datatype.params[n].shadow = msg.value;
|
this.deviceInfo.thingsModels[k].datatype.params[n].shadow = data[j].value;
|
||||||
isComplete = true;
|
isComplete = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -656,7 +656,7 @@ export default {
|
|||||||
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayParams.length; n++) {
|
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayParams.length; n++) {
|
||||||
for (let m = 0; m < this.deviceInfo.thingsModels[k].datatype.arrayParams[n].length; m++) {
|
for (let m = 0; m < this.deviceInfo.thingsModels[k].datatype.arrayParams[n].length; m++) {
|
||||||
if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == data[j].id) {
|
if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == data[j].id) {
|
||||||
this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = msg.value;
|
this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = data[j].value;
|
||||||
isComplete = true;
|
isComplete = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -672,7 +672,7 @@ export default {
|
|||||||
let index = n > 9 ? String(n) : '0' + k;
|
let index = n > 9 ? String(n) : '0' + k;
|
||||||
let prefix = 'array_' + index + '_';
|
let prefix = 'array_' + index + '_';
|
||||||
if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == prefix + data[j].id) {
|
if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == prefix + data[j].id) {
|
||||||
this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = msg.value;
|
this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = data[j].value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,7 +681,7 @@ export default {
|
|||||||
// 整数、小数和字符串类型数组
|
// 整数、小数和字符串类型数组
|
||||||
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayModel.length; n++) {
|
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayModel.length; n++) {
|
||||||
if (this.deviceInfo.thingsModels[k].datatype.arrayModel[n].id == data[j].id) {
|
if (this.deviceInfo.thingsModels[k].datatype.arrayModel[n].id == data[j].id) {
|
||||||
this.deviceInfo.thingsModels[k].datatype.arrayModel[n].shadow = msg.value;
|
this.deviceInfo.thingsModels[k].datatype.arrayModel[n].shadow = data[j].value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -693,24 +693,43 @@ export default {
|
|||||||
if (this.deviceInfo.chartList[k].id.indexOf('array_') == 0) {
|
if (this.deviceInfo.chartList[k].id.indexOf('array_') == 0) {
|
||||||
// 数组类型匹配,例如:array_00_gateway_temperature
|
// 数组类型匹配,例如:array_00_gateway_temperature
|
||||||
if (this.deviceInfo.chartList[k].id == data[j].id) {
|
if (this.deviceInfo.chartList[k].id == data[j].id) {
|
||||||
// let shadows = msg[j].value.split(",");
|
this.deviceInfo.chartList[k].shadow = data[j].value;
|
||||||
this.deviceInfo.chartList[k].shadow = msg.value;
|
|
||||||
// 更新图表
|
// 更新图表
|
||||||
for (let m = 0; m < this.monitorChart.length; m++) {
|
for (let m = 0; m < this.monitorChart.length; m++) {
|
||||||
if (data[j].id == this.monitorChart[m].data.id) {
|
if (data[j].id == this.monitorChart[m].data.id) {
|
||||||
let data = [
|
// 更新数据对象
|
||||||
|
this.monitorChart[m].data.value = data[j].value;
|
||||||
|
|
||||||
|
// 确保数值是数字类型
|
||||||
|
const numValue = Number(data[j].value) || 0;
|
||||||
|
|
||||||
|
// 构建图表数据
|
||||||
|
const chartData = [
|
||||||
{
|
{
|
||||||
value: msg.value,
|
value: numValue,
|
||||||
name: this.monitorChart[m].data.name,
|
name: this.monitorChart[m].data.name,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 更新图表配置,包括detail部分
|
||||||
this.monitorChart[m].chart.setOption({
|
this.monitorChart[m].chart.setOption({
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
data: data,
|
data: chartData,
|
||||||
|
detail: {
|
||||||
|
formatter: '{value}' + ' ' + (this.deviceInfo.chartList[k].datatype.unit || ''),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
}, false); // false表示不合并,完全替换
|
||||||
|
|
||||||
|
// 强制刷新图表
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.monitorChart[m] && this.monitorChart[m].chart) {
|
||||||
|
this.monitorChart[m].chart.resize();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -718,23 +737,43 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
// 普通类型匹配
|
// 普通类型匹配
|
||||||
if (this.deviceInfo.chartList[k].id == data[j].id) {
|
if (this.deviceInfo.chartList[k].id == data[j].id) {
|
||||||
this.deviceInfo.chartList[k].shadow = msg.value;
|
this.deviceInfo.chartList[k].shadow = data[j].value;
|
||||||
// 更新图表
|
// 更新图表
|
||||||
for (let m = 0; m < this.monitorChart.length; m++) {
|
for (let m = 0; m < this.monitorChart.length; m++) {
|
||||||
if (data[j].id == this.monitorChart[m].data.id) {
|
if (data[j].id == this.monitorChart[m].data.id) {
|
||||||
let data = [
|
// 更新数据对象
|
||||||
|
this.monitorChart[m].data.value = data[j].value;
|
||||||
|
|
||||||
|
// 确保数值是数字类型
|
||||||
|
const numValue = Number(data[j].value) || 0;
|
||||||
|
|
||||||
|
// 构建图表数据
|
||||||
|
const chartData = [
|
||||||
{
|
{
|
||||||
value: msg.value,
|
value: numValue,
|
||||||
name: this.monitorChart[m].data.name,
|
name: this.monitorChart[m].data.name,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 更新图表配置,包括detail部分
|
||||||
this.monitorChart[m].chart.setOption({
|
this.monitorChart[m].chart.setOption({
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
data: data,
|
data: chartData,
|
||||||
|
detail: {
|
||||||
|
formatter: '{value}' + ' ' + (this.deviceInfo.chartList[k].datatype.unit || ''),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
}, false); // false表示不合并,完全替换
|
||||||
|
|
||||||
|
// 强制刷新图表
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.monitorChart[m] && this.monitorChart[m].chart) {
|
||||||
|
this.monitorChart[m].chart.resize();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,7 +154,7 @@
|
|||||||
<div class="switch__container" id="switch-c2" :class="{ 'is-hidden': isC2Hidden }">
|
<div class="switch__container" id="switch-c2" :class="{ 'is-hidden': isC2Hidden }">
|
||||||
<div style="display: flex; align-items: center">
|
<div style="display: flex; align-items: center">
|
||||||
<img src="../assets/logo/fastbee.png" alt="logo" style="width: 60px" />
|
<img src="../assets/logo/fastbee.png" alt="logo" style="width: 60px" />
|
||||||
<h2 class="switch__title title" style="margin-left: 10px">FastBee</h2>
|
<h2 class="switch__title title" style="margin-left: 10px">岩安测控</h2>
|
||||||
</div>
|
</div>
|
||||||
<p class="switch__description description">{{ $t('login.989807-39') }}</p>
|
<p class="switch__description description">{{ $t('login.989807-39') }}</p>
|
||||||
<img src="../assets/images/login7.jpeg" alt="logo" style="width: 220px; height: 220px" />
|
<img src="../assets/images/login7.jpeg" alt="logo" style="width: 220px; height: 220px" />
|
||||||
|
|||||||
@ -7,10 +7,10 @@
|
|||||||
<div style="font-size: 42px; padding-top: 40px; width: 300px; margin: 0 auto">
|
<div style="font-size: 42px; padding-top: 40px; width: 300px; margin: 0 auto">
|
||||||
<img :src="logo" alt="logo" style="width: 100px; height: 100px; float: left" />
|
<img :src="logo" alt="logo" style="width: 100px; height: 100px; float: left" />
|
||||||
<div style="float: left; margin-top: 13px; width: 200px; text-align: left">
|
<div style="float: left; margin-top: 13px; width: 200px; text-align: left">
|
||||||
<div>FastBee</div>
|
<div>岩安测控</div>
|
||||||
<div
|
<div
|
||||||
style="letter-spacing: 1.5px; font-size: 20px; font-weight: 600; margin-top: -8px; margin-left: 3px">
|
style="letter-spacing: 1.5px; font-size: 20px; font-weight: 600; margin-top: -8px; margin-left: 3px">
|
||||||
开源物联网平台</div>
|
岩安测控数据平台</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,10 +9,10 @@
|
|||||||
<div style="font-size: 42px; padding-top: 40px; width: 300px; margin: 0 auto">
|
<div style="font-size: 42px; padding-top: 40px; width: 300px; margin: 0 auto">
|
||||||
<img :src="logo" alt="logo" style="width: 100px; height: 100px; float: left" />
|
<img :src="logo" alt="logo" style="width: 100px; height: 100px; float: left" />
|
||||||
<div style="float: left; margin-top: 13px; width: 200px; text-align: left">
|
<div style="float: left; margin-top: 13px; width: 200px; text-align: left">
|
||||||
<div>FastBee</div>
|
<div>岩安测控</div>
|
||||||
<div
|
<div
|
||||||
style="letter-spacing: 1.5px; font-size: 20px; font-weight: 600; margin-top: -8px; margin-left: 3px">
|
style="letter-spacing: 1.5px; font-size: 20px; font-weight: 600; margin-top: -8px; margin-left: 3px">
|
||||||
开源物联网平台</div>
|
岩安测控数据平台</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ function resolve(dir) {
|
|||||||
const CompressionPlugin = require('compression-webpack-plugin');
|
const CompressionPlugin = require('compression-webpack-plugin');
|
||||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
||||||
|
|
||||||
const name = process.env.VUE_APP_TITLE || 'FastBee物联网平台'; // 网页标题
|
const name = process.env.VUE_APP_TITLE || '岩安测控数据平台'; // 网页标题
|
||||||
|
|
||||||
const port = process.env.port || process.env.npm_config_port || 80; // 端口
|
const port = process.env.port || process.env.npm_config_port || 80; // 端口
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user