添加PSDK的配置
This commit is contained in:
parent
6292b3f9b6
commit
7ce75685aa
|
|
@ -37,4 +37,22 @@ public interface IThingsBoardDomain {
|
||||||
* @return 类型安全的遥测数据映射
|
* @return 类型安全的遥测数据映射
|
||||||
*/
|
*/
|
||||||
TelemetryMap getDeviceTelemetry(String deviceId);
|
TelemetryMap getDeviceTelemetry(String deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据设备ID获取设备的预定义属性
|
||||||
|
* 只返回在 DeviceAttributes 中预定义的属性
|
||||||
|
*
|
||||||
|
* @param deviceId 设备ID
|
||||||
|
* @return 类型安全的属性映射,只包含预定义的属性
|
||||||
|
*/
|
||||||
|
AttributeMap getPredefinedDeviceAttributes(String deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据设备ID获取设备的预定义遥测数据
|
||||||
|
* 只返回在 DeviceTelemetry 中预定义的遥测数据
|
||||||
|
*
|
||||||
|
* @param deviceId 设备ID
|
||||||
|
* @return 类型安全的遥测数据映射,只包含预定义的遥测数据
|
||||||
|
*/
|
||||||
|
TelemetryMap getPredefinedDeviceTelemetry(String deviceId);
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ package com.ruoyi.device.domain.impl;
|
||||||
import com.ruoyi.device.domain.api.IThingsBoardDomain;
|
import com.ruoyi.device.domain.api.IThingsBoardDomain;
|
||||||
import com.ruoyi.device.domain.model.thingsboard.*;
|
import com.ruoyi.device.domain.model.thingsboard.*;
|
||||||
import com.ruoyi.device.domain.model.thingsboard.constants.DeviceAttributes;
|
import com.ruoyi.device.domain.model.thingsboard.constants.DeviceAttributes;
|
||||||
|
import com.ruoyi.device.domain.model.thingsboard.constants.DeviceTelemetry;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
@ -113,6 +114,65 @@ public class ThingsBoardDomainImpl implements IThingsBoardDomain {
|
||||||
return telemetryMap;
|
return telemetryMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AttributeMap getPredefinedDeviceAttributes(String deviceId) {
|
||||||
|
// 先获取所有属性(已经处理了异常情况)
|
||||||
|
AttributeMap allAttributes = getDeviceAttributes(deviceId);
|
||||||
|
|
||||||
|
// 创建新的 AttributeMap 只包含预定义的键
|
||||||
|
AttributeMap predefinedAttributes = new AttributeMap();
|
||||||
|
|
||||||
|
// 获取预定义的键名称集合
|
||||||
|
List<String> predefinedKeyNames = DeviceAttributes.getPredefinedKeys()
|
||||||
|
.stream()
|
||||||
|
.map(AttributeKey::getName)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 过滤:只保留预定义的键
|
||||||
|
for (AttributeKey<?> key : allAttributes.keySet()) {
|
||||||
|
if (predefinedKeyNames.contains(key.getName())) {
|
||||||
|
// 复制到新的 map
|
||||||
|
allAttributes.get(key).ifPresent(value -> {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
AttributeKey<Object> objKey = (AttributeKey<Object>) key;
|
||||||
|
predefinedAttributes.put(objKey, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return predefinedAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TelemetryMap getPredefinedDeviceTelemetry(String deviceId) {
|
||||||
|
// 先获取所有遥测数据(已经处理了 null 值问题)
|
||||||
|
TelemetryMap allTelemetry = getDeviceTelemetry(deviceId);
|
||||||
|
|
||||||
|
// 创建新的 TelemetryMap 只包含预定义的键
|
||||||
|
TelemetryMap predefinedTelemetry = new TelemetryMap();
|
||||||
|
|
||||||
|
// 获取预定义的键名称集合
|
||||||
|
List<String> predefinedKeyNames = DeviceTelemetry.getPredefinedKeys()
|
||||||
|
.stream()
|
||||||
|
.map(TelemetryKey::getName)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 过滤:只保留预定义的键
|
||||||
|
for (TelemetryKey<?> key : allTelemetry.keySet()) {
|
||||||
|
if (predefinedKeyNames.contains(key.getName())) {
|
||||||
|
// 复制到新的 map
|
||||||
|
allTelemetry.get(key).ifPresent(telemetryValue -> {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
TelemetryKey<Object> objKey = (TelemetryKey<Object>) key;
|
||||||
|
predefinedTelemetry.put(objKey, telemetryValue.getValue(), telemetryValue.getTimestamp());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return predefinedTelemetry;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析属性并添加到AttributeMap
|
* 解析属性并添加到AttributeMap
|
||||||
* 使用延迟注册机制,自动处理所有属性
|
* 使用延迟注册机制,自动处理所有属性
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
package com.ruoyi.device.domain.model.thingsboard;
|
package com.ruoyi.device.domain.model.thingsboard;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类型安全的属性键
|
* 类型安全的属性键
|
||||||
|
|
@ -15,7 +14,7 @@ public class AttributeKey<T> {
|
||||||
private final Class<T> type;
|
private final Class<T> type;
|
||||||
private final ValueParser<T> parser;
|
private final ValueParser<T> parser;
|
||||||
|
|
||||||
private static final Map<String, AttributeKey<?>> REGISTRY = new HashMap<>();
|
private static final Map<String, AttributeKey<?>> REGISTRY = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private AttributeKey(String name, Class<T> type, ValueParser<T> parser) {
|
private AttributeKey(String name, Class<T> type, ValueParser<T> parser) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
@ -51,6 +50,15 @@ public class AttributeKey<T> {
|
||||||
return REGISTRY.computeIfAbsent(name, k -> inferKeyFromValue(name, value));
|
return REGISTRY.computeIfAbsent(name, k -> inferKeyFromValue(name, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有已注册的属性键集合
|
||||||
|
*
|
||||||
|
* @return 所有已注册的属性键
|
||||||
|
*/
|
||||||
|
public static Map<String, AttributeKey<?>> getAllRegisteredKeys() {
|
||||||
|
return new ConcurrentHashMap<>(REGISTRY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析原始值为目标类型
|
* 解析原始值为目标类型
|
||||||
*/
|
*/
|
||||||
|
|
@ -76,36 +84,37 @@ public class AttributeKey<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据值自动推断类型并创建属性键
|
* 根据值自动推断类型并创建属性键
|
||||||
|
* 注意:这个方法不能调用 of() 方法,因为会导致递归更新
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static <T> AttributeKey<T> inferKeyFromValue(String name, Object value) {
|
private static <T> AttributeKey<T> inferKeyFromValue(String name, Object value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
// 默认为 String 类型
|
// 默认为 String 类型
|
||||||
return (AttributeKey<T>) of(name, String.class, v -> v != null ? v.toString() : null);
|
return (AttributeKey<T>) new AttributeKey<>(name, String.class, v -> v != null ? v.toString() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据值的实际类型推断
|
// 根据值的实际类型推断
|
||||||
if (value instanceof Boolean) {
|
if (value instanceof Boolean) {
|
||||||
return (AttributeKey<T>) of(name, Boolean.class, v -> {
|
return (AttributeKey<T>) new AttributeKey<>(name, Boolean.class, v -> {
|
||||||
if (v == null) return null;
|
if (v == null) return null;
|
||||||
if (v instanceof Boolean) return (Boolean) v;
|
if (v instanceof Boolean) return (Boolean) v;
|
||||||
return Boolean.parseBoolean(v.toString());
|
return Boolean.parseBoolean(v.toString());
|
||||||
});
|
});
|
||||||
} else if (value instanceof Long || value instanceof Integer) {
|
} else if (value instanceof Long || value instanceof Integer) {
|
||||||
return (AttributeKey<T>) of(name, Long.class, v -> {
|
return (AttributeKey<T>) new AttributeKey<>(name, Long.class, v -> {
|
||||||
if (v == null) return null;
|
if (v == null) return null;
|
||||||
if (v instanceof Number) return ((Number) v).longValue();
|
if (v instanceof Number) return ((Number) v).longValue();
|
||||||
return Long.parseLong(v.toString());
|
return Long.parseLong(v.toString());
|
||||||
});
|
});
|
||||||
} else if (value instanceof Double || value instanceof Float) {
|
} else if (value instanceof Double || value instanceof Float) {
|
||||||
return (AttributeKey<T>) of(name, Double.class, v -> {
|
return (AttributeKey<T>) new AttributeKey<>(name, Double.class, v -> {
|
||||||
if (v == null) return null;
|
if (v == null) return null;
|
||||||
if (v instanceof Number) return ((Number) v).doubleValue();
|
if (v instanceof Number) return ((Number) v).doubleValue();
|
||||||
return Double.parseDouble(v.toString());
|
return Double.parseDouble(v.toString());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 默认为 String 类型
|
// 默认为 String 类型
|
||||||
return (AttributeKey<T>) of(name, String.class, v -> v != null ? v.toString() : null);
|
return (AttributeKey<T>) new AttributeKey<>(name, String.class, v -> v != null ? v.toString() : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
package com.ruoyi.device.domain.model.thingsboard;
|
package com.ruoyi.device.domain.model.thingsboard;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.ruoyi.device.domain.model.thingsboard.attributes.psdk.PsdkDevice;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类型安全的遥测数据键
|
* 类型安全的遥测数据键
|
||||||
|
|
@ -15,7 +19,7 @@ public class TelemetryKey<T> {
|
||||||
private final Class<T> type;
|
private final Class<T> type;
|
||||||
private final ValueParser<T> parser;
|
private final ValueParser<T> parser;
|
||||||
|
|
||||||
private static final Map<String, TelemetryKey<?>> REGISTRY = new HashMap<>();
|
private static final Map<String, TelemetryKey<?>> REGISTRY = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private TelemetryKey(String name, Class<T> type, ValueParser<T> parser) {
|
private TelemetryKey(String name, Class<T> type, ValueParser<T> parser) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
@ -51,6 +55,15 @@ public class TelemetryKey<T> {
|
||||||
return REGISTRY.computeIfAbsent(name, k -> inferKeyFromValue(name, value));
|
return REGISTRY.computeIfAbsent(name, k -> inferKeyFromValue(name, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有已注册的遥测键集合
|
||||||
|
*
|
||||||
|
* @return 所有已注册的遥测键
|
||||||
|
*/
|
||||||
|
public static Map<String, TelemetryKey<?>> getAllRegisteredKeys() {
|
||||||
|
return new ConcurrentHashMap<>(REGISTRY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析原始值为目标类型
|
* 解析原始值为目标类型
|
||||||
*/
|
*/
|
||||||
|
|
@ -76,39 +89,100 @@ public class TelemetryKey<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据值自动推断类型并创建遥测键
|
* 根据值自动推断类型并创建遥测键
|
||||||
|
* 注意:这个方法不能调用 of() 方法,因为会导致递归更新
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static <T> TelemetryKey<T> inferKeyFromValue(String name, Object value) {
|
private static <T> TelemetryKey<T> inferKeyFromValue(String name, Object value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
// 默认为 String 类型
|
// 默认为 String 类型
|
||||||
return (TelemetryKey<T>) of(name, String.class, v -> v != null ? v.toString() : null);
|
return (TelemetryKey<T>) new TelemetryKey<>(name, String.class, v -> v != null ? v.toString() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊处理:psdk_widget_values 字段
|
||||||
|
if ("psdk_widget_values".equals(name)) {
|
||||||
|
return (TelemetryKey<T>) createPsdkWidgetValuesKeyWithoutRegistering();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据值的实际类型推断
|
// 根据值的实际类型推断
|
||||||
if (value instanceof Boolean) {
|
if (value instanceof Boolean) {
|
||||||
return (TelemetryKey<T>) of(name, Boolean.class, v -> {
|
return (TelemetryKey<T>) new TelemetryKey<>(name, Boolean.class, v -> {
|
||||||
if (v == null) return null;
|
if (v == null) return null;
|
||||||
if (v instanceof Boolean) return (Boolean) v;
|
if (v instanceof Boolean) return (Boolean) v;
|
||||||
return Boolean.parseBoolean(v.toString());
|
return Boolean.parseBoolean(v.toString());
|
||||||
});
|
});
|
||||||
} else if (value instanceof Long || value instanceof Integer) {
|
} else if (value instanceof Long || value instanceof Integer) {
|
||||||
return (TelemetryKey<T>) of(name, Long.class, v -> {
|
return (TelemetryKey<T>) new TelemetryKey<>(name, Long.class, v -> {
|
||||||
if (v == null) return null;
|
if (v == null) return null;
|
||||||
if (v instanceof Number) return ((Number) v).longValue();
|
if (v instanceof Number) return ((Number) v).longValue();
|
||||||
return Long.parseLong(v.toString());
|
return Long.parseLong(v.toString());
|
||||||
});
|
});
|
||||||
} else if (value instanceof Double || value instanceof Float) {
|
} else if (value instanceof Double || value instanceof Float) {
|
||||||
return (TelemetryKey<T>) of(name, Double.class, v -> {
|
return (TelemetryKey<T>) new TelemetryKey<>(name, Double.class, v -> {
|
||||||
if (v == null) return null;
|
if (v == null) return null;
|
||||||
if (v instanceof Number) return ((Number) v).doubleValue();
|
if (v instanceof Number) return ((Number) v).doubleValue();
|
||||||
return Double.parseDouble(v.toString());
|
return Double.parseDouble(v.toString());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 默认为 String 类型
|
// 默认为 String 类型
|
||||||
return (TelemetryKey<T>) of(name, String.class, v -> v != null ? v.toString() : null);
|
return (TelemetryKey<T>) new TelemetryKey<>(name, String.class, v -> v != null ? v.toString() : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 psdk_widget_values 的遥测键(不注册到 REGISTRY)
|
||||||
|
* 用于解析 Python 风格的字典字符串为 List<PsdkDevice>
|
||||||
|
* 注意:不能调用 of() 方法,避免递归更新
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static TelemetryKey<List<PsdkDevice>> createPsdkWidgetValuesKeyWithoutRegistering() {
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
// 配置 ObjectMapper 忽略未知字段
|
||||||
|
objectMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
|
||||||
|
return new TelemetryKey<>(
|
||||||
|
"psdk_widget_values",
|
||||||
|
(Class<List<PsdkDevice>>) (Class<?>) List.class,
|
||||||
|
value -> {
|
||||||
|
if (value == null) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 如果已经是 List<PsdkDevice> 类型,直接返回
|
||||||
|
if (value instanceof List) {
|
||||||
|
return (List<PsdkDevice>) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是字符串,需要处理 Python 风格的字典格式
|
||||||
|
if (value instanceof String) {
|
||||||
|
String jsonStr = (String) value;
|
||||||
|
|
||||||
|
// 将 Python 风格的字典转换为标准 JSON 格式
|
||||||
|
// 1. 将单引号替换为双引号
|
||||||
|
jsonStr = jsonStr.replace("'", "\"");
|
||||||
|
|
||||||
|
// 2. 处理 True/False/None (如果有的话)
|
||||||
|
jsonStr = jsonStr.replace(": True", ": true")
|
||||||
|
.replace(": False", ": false")
|
||||||
|
.replace(": None", ": null");
|
||||||
|
|
||||||
|
return objectMapper.readValue(
|
||||||
|
jsonStr,
|
||||||
|
new TypeReference<List<PsdkDevice>>() {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是其他对象(如 JsonNode),转换为 JSON 再解析
|
||||||
|
String json = objectMapper.writeValueAsString(value);
|
||||||
|
return objectMapper.readValue(
|
||||||
|
json,
|
||||||
|
new TypeReference<List<PsdkDevice>>() {}
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to parse psdk_widget_values: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 值解析器接口
|
* 值解析器接口
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
package com.ruoyi.device.domain.model.thingsboard.attributes.psdk;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PSDK
|
||||||
|
*/
|
||||||
|
public class PsdkDevice {
|
||||||
|
private int psdk_index;
|
||||||
|
private String psdk_lib_version;
|
||||||
|
private String psdk_name;
|
||||||
|
private String psdk_sn;
|
||||||
|
private String psdk_version;
|
||||||
|
private Speaker speaker;
|
||||||
|
private List<ValueItem> values;
|
||||||
|
|
||||||
|
// 构造方法
|
||||||
|
public PsdkDevice() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter和Setter方法
|
||||||
|
public int getPsdk_index() {
|
||||||
|
return psdk_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPsdk_index(int psdk_index) {
|
||||||
|
this.psdk_index = psdk_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPsdk_lib_version() {
|
||||||
|
return psdk_lib_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPsdk_lib_version(String psdk_lib_version) {
|
||||||
|
this.psdk_lib_version = psdk_lib_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPsdk_name() {
|
||||||
|
return psdk_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPsdk_name(String psdk_name) {
|
||||||
|
this.psdk_name = psdk_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPsdk_sn() {
|
||||||
|
return psdk_sn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPsdk_sn(String psdk_sn) {
|
||||||
|
this.psdk_sn = psdk_sn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPsdk_version() {
|
||||||
|
return psdk_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPsdk_version(String psdk_version) {
|
||||||
|
this.psdk_version = psdk_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Speaker getSpeaker() {
|
||||||
|
return speaker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpeaker(Speaker speaker) {
|
||||||
|
this.speaker = speaker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ValueItem> getValues() {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValues(List<ValueItem> values) {
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "PsdkDevice{" +
|
||||||
|
"psdk_index=" + psdk_index +
|
||||||
|
", psdk_lib_version='" + psdk_lib_version + '\'' +
|
||||||
|
", psdk_name='" + psdk_name + '\'' +
|
||||||
|
", psdk_sn='" + psdk_sn + '\'' +
|
||||||
|
", psdk_version='" + psdk_version + '\'' +
|
||||||
|
", speaker=" + speaker +
|
||||||
|
", values=" + values +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.ruoyi.device.domain.model.thingsboard.attributes.psdk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speaker
|
||||||
|
*/
|
||||||
|
public class Speaker {
|
||||||
|
private String play_file_md5;
|
||||||
|
private String play_file_name;
|
||||||
|
private int play_mode;
|
||||||
|
private int play_volume;
|
||||||
|
private int system_state;
|
||||||
|
private int work_mode;
|
||||||
|
|
||||||
|
// 构造方法
|
||||||
|
public Speaker() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter和Setter方法
|
||||||
|
public String getPlay_file_md5() {
|
||||||
|
return play_file_md5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlay_file_md5(String play_file_md5) {
|
||||||
|
this.play_file_md5 = play_file_md5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlay_file_name() {
|
||||||
|
return play_file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlay_file_name(String play_file_name) {
|
||||||
|
this.play_file_name = play_file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPlay_mode() {
|
||||||
|
return play_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlay_mode(int play_mode) {
|
||||||
|
this.play_mode = play_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPlay_volume() {
|
||||||
|
return play_volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlay_volume(int play_volume) {
|
||||||
|
this.play_volume = play_volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSystem_state() {
|
||||||
|
return system_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSystem_state(int system_state) {
|
||||||
|
this.system_state = system_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWork_mode() {
|
||||||
|
return work_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWork_mode(int work_mode) {
|
||||||
|
this.work_mode = work_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Speaker{" +
|
||||||
|
"play_file_md5='" + play_file_md5 + '\'' +
|
||||||
|
", play_file_name='" + play_file_name + '\'' +
|
||||||
|
", play_mode=" + play_mode +
|
||||||
|
", play_volume=" + play_volume +
|
||||||
|
", system_state=" + system_state +
|
||||||
|
", work_mode=" + work_mode +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.ruoyi.device.domain.model.thingsboard.attributes.psdk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValueItem
|
||||||
|
*/
|
||||||
|
public class ValueItem {
|
||||||
|
private int index;
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
// 构造方法
|
||||||
|
public ValueItem() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueItem(int index, int value) {
|
||||||
|
this.index = index;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter和Setter方法
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ValueItem{" +
|
||||||
|
"index=" + index +
|
||||||
|
", value=" + value +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,9 @@ package com.ruoyi.device.domain.model.thingsboard.constants;
|
||||||
|
|
||||||
import com.ruoyi.device.domain.model.thingsboard.AttributeKey;
|
import com.ruoyi.device.domain.model.thingsboard.AttributeKey;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预定义的设备属性键
|
* 预定义的设备属性键
|
||||||
* 使用类型安全的方式定义常用属性
|
* 使用类型安全的方式定义常用属性
|
||||||
|
|
@ -22,20 +25,13 @@ public class DeviceAttributes {
|
||||||
value -> value != null ? value.toString() : null
|
value -> value != null ? value.toString() : null
|
||||||
);
|
);
|
||||||
|
|
||||||
// 网关名称 - String
|
// 网关 - String
|
||||||
public static final AttributeKey<String> GATEWAY = AttributeKey.of(
|
public static final AttributeKey<String> GATEWAY = AttributeKey.of(
|
||||||
"gateway",
|
"gateway",
|
||||||
String.class,
|
String.class,
|
||||||
value -> value != null ? value.toString() : null
|
value -> value != null ? value.toString() : null
|
||||||
);
|
);
|
||||||
|
|
||||||
// 机场SN - String
|
|
||||||
public static final AttributeKey<String> DOCK_SN = AttributeKey.of(
|
|
||||||
"dock_sn",
|
|
||||||
String.class,
|
|
||||||
value -> value != null ? value.toString() : null
|
|
||||||
);
|
|
||||||
|
|
||||||
// 最后连接时间 - Long
|
// 最后连接时间 - Long
|
||||||
public static final AttributeKey<Long> LAST_CONNECT_TIME = AttributeKey.of(
|
public static final AttributeKey<Long> LAST_CONNECT_TIME = AttributeKey.of(
|
||||||
"lastConnectTime",
|
"lastConnectTime",
|
||||||
|
|
@ -92,6 +88,23 @@ public class DeviceAttributes {
|
||||||
// 工具类,禁止实例化
|
// 工具类,禁止实例化
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有预定义的属性键
|
||||||
|
*
|
||||||
|
* @return 预定义的属性键列表
|
||||||
|
*/
|
||||||
|
public static List<AttributeKey<?>> getPredefinedKeys() {
|
||||||
|
return Arrays.asList(
|
||||||
|
CONNECTOR_TYPE,
|
||||||
|
CONNECTOR_NAME,
|
||||||
|
GATEWAY,
|
||||||
|
LAST_CONNECT_TIME,
|
||||||
|
ACTIVE,
|
||||||
|
LAST_ACTIVITY_TIME,
|
||||||
|
LAST_DISCONNECT_TIME
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化所有属性键
|
* 初始化所有属性键
|
||||||
* 确保所有静态字段被加载和注册
|
* 确保所有静态字段被加载和注册
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
package com.ruoyi.device.domain.model.thingsboard.constants;
|
package com.ruoyi.device.domain.model.thingsboard.constants;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.ruoyi.device.domain.model.thingsboard.TelemetryKey;
|
import com.ruoyi.device.domain.model.thingsboard.TelemetryKey;
|
||||||
|
import com.ruoyi.device.domain.model.thingsboard.attributes.psdk.PsdkDevice;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预定义的设备遥测数据键
|
* 预定义的设备遥测数据键
|
||||||
|
|
@ -8,6 +15,10 @@ import com.ruoyi.device.domain.model.thingsboard.TelemetryKey;
|
||||||
*/
|
*/
|
||||||
public class DeviceTelemetry {
|
public class DeviceTelemetry {
|
||||||
|
|
||||||
|
// Jackson ObjectMapper 用于 JSON 解析
|
||||||
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
|
||||||
// 空调状态 - Integer
|
// 空调状态 - Integer
|
||||||
public static final TelemetryKey<Integer> AIR_CONDITIONER_STATE = TelemetryKey.of(
|
public static final TelemetryKey<Integer> AIR_CONDITIONER_STATE = TelemetryKey.of(
|
||||||
"air_conditioner.air_conditioner_state",
|
"air_conditioner.air_conditioner_state",
|
||||||
|
|
@ -47,10 +58,69 @@ public class DeviceTelemetry {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// PSDK Widget Values - List<PsdkDevice>
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static final TelemetryKey<List<PsdkDevice>> PSDK_WIDGET_VALUES = TelemetryKey.of(
|
||||||
|
"psdk_widget_values",
|
||||||
|
(Class<List<PsdkDevice>>) (Class<?>) List.class,
|
||||||
|
value -> {
|
||||||
|
if (value == null) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 如果已经是 List<PsdkDevice> 类型,直接返回
|
||||||
|
if (value instanceof List) {
|
||||||
|
return (List<PsdkDevice>) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是字符串,需要处理 Python 风格的字典格式
|
||||||
|
if (value instanceof String) {
|
||||||
|
String jsonStr = (String) value;
|
||||||
|
|
||||||
|
// 将 Python 风格的字典转换为标准 JSON 格式
|
||||||
|
// 1. 将单引号替换为双引号
|
||||||
|
jsonStr = jsonStr.replace("'", "\"");
|
||||||
|
|
||||||
|
// 2. 处理 True/False/None (如果有的话)
|
||||||
|
jsonStr = jsonStr.replace(": True", ": true")
|
||||||
|
.replace(": False", ": false")
|
||||||
|
.replace(": None", ": null");
|
||||||
|
|
||||||
|
return OBJECT_MAPPER.readValue(
|
||||||
|
jsonStr,
|
||||||
|
new TypeReference<List<PsdkDevice>>() {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是其他对象(如 JsonNode),转换为 JSON 再解析
|
||||||
|
String json = OBJECT_MAPPER.writeValueAsString(value);
|
||||||
|
return OBJECT_MAPPER.readValue(
|
||||||
|
json,
|
||||||
|
new TypeReference<List<PsdkDevice>>() {}
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to parse psdk_widget_values: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
private DeviceTelemetry() {
|
private DeviceTelemetry() {
|
||||||
// 工具类,禁止实例化
|
// 工具类,禁止实例化
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有预定义的遥测键
|
||||||
|
*
|
||||||
|
* @return 预定义的遥测键列表
|
||||||
|
*/
|
||||||
|
public static List<TelemetryKey<?>> getPredefinedKeys() {
|
||||||
|
return Arrays.asList(
|
||||||
|
AIR_CONDITIONER_STATE,
|
||||||
|
TEMPERATURE,
|
||||||
|
HUMIDITY,
|
||||||
|
PSDK_WIDGET_VALUES
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化所有遥测键
|
* 初始化所有遥测键
|
||||||
* 确保所有静态字段被加载和注册
|
* 确保所有静态字段被加载和注册
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue