202 lines
7.6 KiB
Java
202 lines
7.6 KiB
Java
package com.ruoyi.device.domain.impl;
|
||
|
||
|
||
import com.ruoyi.device.domain.api.IThingsBoardDomain;
|
||
import com.ruoyi.device.domain.model.thingsboard.*;
|
||
import com.ruoyi.device.domain.model.thingsboard.constants.DeviceAttributes;
|
||
import org.slf4j.Logger;
|
||
import org.slf4j.LoggerFactory;
|
||
import org.springframework.beans.factory.annotation.Value;
|
||
import org.springframework.scheduling.annotation.Scheduled;
|
||
import org.springframework.stereotype.Component;
|
||
import org.thingsboard.rest.client.RestClient;
|
||
import org.thingsboard.server.common.data.id.DeviceId;
|
||
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
||
import org.thingsboard.server.common.data.kv.TsKvEntry;
|
||
|
||
import java.util.List;
|
||
import java.util.Optional;
|
||
import java.util.UUID;
|
||
|
||
/**
|
||
* ThingsBoard设备服务实现类
|
||
*/
|
||
@Component
|
||
public class ThingsBoardDomainImpl implements IThingsBoardDomain {
|
||
|
||
|
||
|
||
private static final Logger log = LoggerFactory.getLogger(ThingsBoardDomainImpl.class);
|
||
|
||
private final RestClient client;
|
||
private final int pageSize;
|
||
|
||
/**
|
||
* 构造函数 - Spring 会自动装配
|
||
* @param clientManager RestClient 管理器
|
||
* @param pageSize 分页大小,从配置文件读取,默认值为 10
|
||
*/
|
||
public ThingsBoardDomainImpl(RestClientManager clientManager,
|
||
@Value("${thingsboard.page-size:10}") int pageSize) {
|
||
this.client = clientManager.getClient();
|
||
this.pageSize = pageSize;
|
||
}
|
||
|
||
@Override
|
||
public Iterable<List<DeviceInfo>> getAllDevices() {
|
||
return new DeviceIterator(client, pageSize);
|
||
}
|
||
|
||
@Override
|
||
public AttributeMap getDeviceAttributes(String deviceId) {
|
||
AttributeMap attributeMap = new AttributeMap();
|
||
|
||
try {
|
||
DeviceId id = new DeviceId(UUID.fromString(deviceId));
|
||
|
||
// 获取所有属性键
|
||
List<String> attributeKeys = client.getAttributeKeys(id);
|
||
if (attributeKeys == null || attributeKeys.isEmpty()) {
|
||
log.debug("设备 {} 没有属性", deviceId);
|
||
return attributeMap;
|
||
}
|
||
|
||
// 获取属性值
|
||
List<AttributeKvEntry> attributeKvEntries = client.getAttributeKvEntries(id, attributeKeys);
|
||
if (attributeKvEntries == null || attributeKvEntries.isEmpty()) {
|
||
log.debug("设备 {} 的属性值为空", deviceId);
|
||
return attributeMap;
|
||
}
|
||
|
||
// 解析并填充到AttributeMap
|
||
for (AttributeKvEntry entry : attributeKvEntries) {
|
||
parseAndPutAttribute(attributeMap, entry);
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
log.error("获取设备属性失败: deviceId={}, error={}", deviceId, e.getMessage(), e);
|
||
}
|
||
|
||
return attributeMap;
|
||
}
|
||
|
||
@Override
|
||
public TelemetryMap getDeviceTelemetry(String deviceId) {
|
||
TelemetryMap telemetryMap = new TelemetryMap();
|
||
|
||
try {
|
||
DeviceId id = new DeviceId(UUID.fromString(deviceId));
|
||
|
||
// 获取所有遥测键
|
||
List<String> timeseriesKeys = client.getTimeseriesKeys(id);
|
||
if (timeseriesKeys == null || timeseriesKeys.isEmpty()) {
|
||
log.debug("设备 {} 没有遥测数据", deviceId);
|
||
return telemetryMap;
|
||
}
|
||
|
||
// 获取最新的遥测数据
|
||
List<TsKvEntry> latestTimeseries = client.getLatestTimeseries(id, timeseriesKeys);
|
||
if (latestTimeseries == null || latestTimeseries.isEmpty()) {
|
||
log.debug("设备 {} 的遥测数据为空", deviceId);
|
||
return telemetryMap;
|
||
}
|
||
|
||
// 解析并填充到TelemetryMap
|
||
for (TsKvEntry entry : latestTimeseries) {
|
||
parseAndPutTelemetry(telemetryMap, entry);
|
||
}
|
||
|
||
} catch (Exception e) {
|
||
log.error("获取设备遥测数据失败: deviceId={}, error={}", deviceId, e.getMessage(), e);
|
||
}
|
||
|
||
return telemetryMap;
|
||
}
|
||
|
||
/**
|
||
* 解析属性并添加到AttributeMap
|
||
* 使用延迟注册机制,自动处理所有属性
|
||
*/
|
||
@SuppressWarnings("unchecked")
|
||
private void parseAndPutAttribute(AttributeMap attributeMap, AttributeKvEntry entry) {
|
||
String keyName = entry.getKey();
|
||
Object value = entry.getValue();
|
||
|
||
try {
|
||
// 使用延迟注册机制:如果键不存在则自动创建
|
||
AttributeKey<?> key = AttributeKey.getOrCreate(keyName, value);
|
||
|
||
// 使用键的解析器解析值
|
||
Object parsedValue = ((AttributeKey<Object>) key).parse(value);
|
||
attributeMap.put((AttributeKey<Object>) key, parsedValue);
|
||
log.debug("成功解析属性: {} = {} (type: {})", keyName, parsedValue, key.getType().getSimpleName());
|
||
} catch (Exception e) {
|
||
log.warn("解析属性失败: key={}, value={}, error={}", keyName, value, e.getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 解析遥测数据并添加到TelemetryMap
|
||
* 使用延迟注册机制,自动处理所有遥测数据
|
||
*/
|
||
@SuppressWarnings("unchecked")
|
||
private void parseAndPutTelemetry(TelemetryMap telemetryMap, TsKvEntry entry) {
|
||
String keyName = entry.getKey();
|
||
Object value = entry.getValue();
|
||
|
||
try {
|
||
// 使用延迟注册机制:如果键不存在则自动创建
|
||
TelemetryKey<?> key = TelemetryKey.getOrCreate(keyName, value);
|
||
|
||
// 使用键的解析器解析值
|
||
Object parsedValue = ((TelemetryKey<Object>) key).parse(value);
|
||
telemetryMap.put((TelemetryKey<Object>) key, parsedValue, entry.getTs());
|
||
log.debug("成功解析遥测数据: {} = {} (timestamp: {}, type: {})",
|
||
keyName, parsedValue, entry.getTs(), key.getType().getSimpleName());
|
||
} catch (Exception e) {
|
||
log.warn("解析遥测数据失败: key={}, value={}, error={}", keyName, value, e.getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 定时任务:每隔1分钟打印所有设备信息
|
||
* 执行时间:每分钟的第0秒执行
|
||
*/
|
||
@Scheduled(cron = "0 * * * * ?")
|
||
public void printAllDevicesScheduled() {
|
||
try {
|
||
log.info("========== 开始执行定时任务:打印所有设备信息 ==========");
|
||
|
||
Iterable<List<DeviceInfo>> allDevices = getAllDevices();
|
||
int totalCount = 0;
|
||
|
||
for (List<DeviceInfo> deviceBatch : allDevices) {
|
||
for (DeviceInfo device : deviceBatch) {
|
||
// 获取设备属性以获取活跃状态
|
||
Boolean activeStatus = false;
|
||
try {
|
||
AttributeMap attributes = getDeviceAttributes(device.getId());
|
||
// 尝试从 AttributeMap 中获取 active 属性
|
||
Optional<Boolean> active = attributes.get(DeviceAttributes.ACTIVE);
|
||
if (active.isPresent()) {
|
||
activeStatus = active.get();
|
||
}
|
||
} catch (Exception e) {
|
||
log.debug("获取设备 {} 的活跃状态失败: {}", device.getId(), e.getMessage());
|
||
}
|
||
|
||
log.info("Device Name: {}, Device ID: {}, Device Type: {}, Active: {}",
|
||
device.getName(),
|
||
device.getId(),
|
||
device.getType(),
|
||
activeStatus);
|
||
totalCount++;
|
||
}
|
||
}
|
||
|
||
log.info("========== 定时任务执行完成,共打印 {} 个设备 ==========", totalCount);
|
||
} catch (Exception e) {
|
||
log.error("定时任务执行失败: {}", e.getMessage(), e);
|
||
}
|
||
}
|
||
} |