新增IoT设备工牌控制器及数据接口

This commit is contained in:
syruan 2025-05-27 09:11:26 +08:00
parent 9a52a584ee
commit 01085ecfd2
5 changed files with 1033 additions and 0 deletions

View File

@ -0,0 +1,42 @@
package com.bonus.material.iot.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* RestTemplate配置类
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// 配置消息转换器
List<HttpMessageConverter<?>> converters = new ArrayList<>();
// String转换器支持各种内容类型
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
stringConverter.setWriteAcceptCharset(false);
converters.add(stringConverter);
// JSON转换器
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
jsonConverter.setObjectMapper(new ObjectMapper());
converters.add(jsonConverter);
restTemplate.setMessageConverters(converters);
return restTemplate;
}
}

View File

@ -0,0 +1,645 @@
package com.bonus.material.iot.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author : 阮世耀
* @version : 1.0
* @PackagePath: com.bonus.material.iot.controller
* @CreateTime: 2025-05-26 17:23
* @Description: IoT设备徽章控制器用于获取设备信息
*/
@RestController
@RequestMapping("/iot/badge")
public class IotBadgeController {
@Autowired
private RestTemplate restTemplate;
private final ObjectMapper objectMapper = new ObjectMapper();
// 登录账号配置常量
private static final String DEFAULT_LOGIN_NAME = "gzzbgsgk";
private static final String DEFAULT_LOGIN_PASSWORD = "123456";
// 使用ConcurrentMap存储令牌和缓存数据
private final ConcurrentHashMap<String, Object> tokenCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Object> deviceCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Long> cacheTimestamps = new ConcurrentHashMap<>();
// 缓存过期时间常量
private static final long TOKEN_EXPIRE_TIME = 20 * 60 * 1000; // 令牌缓存时间20分钟
private static final long DEVICE_CACHE_TIME = 5 * 60 * 1000; // 设备缓存时间5分钟
// 定时任务执行器
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 固定的设备ID列表后期可以修改
private static final String[] DEVICE_IDS = {
"d7e84fd73e264431ba46303014431ade", "554778b69f394b899f1f2baff023c5be",
"061004dcc750463c974b6fe8be2fc508", "94cd6567b2924174a8526064a2a3d659",
"51679b90409940898e68691eb37f9370", "9ed65c5f6fad4a0ab058cab2b959080b",
"548abdd848f641a9a8afd8f6eb65856c", "5d66b87f5eec4661b6b6aa00e43699f8"
};
// 初始化定时清理任务
// {
// scheduler.scheduleAtFixedRate(this::cleanExpiredCache, 1, 1, TimeUnit.MINUTES);
// }
/**
* 清理过期缓存
*/
private void cleanExpiredCache() {
long currentTime = System.currentTimeMillis();
// 清理过期的令牌
Long tokenTime = cacheTimestamps.get("token");
if (tokenTime != null && currentTime - tokenTime > TOKEN_EXPIRE_TIME) {
tokenCache.remove("currentToken");
tokenCache.remove("currentUserId");
cacheTimestamps.remove("token");
}
// 清理过期的设备缓存
deviceCache.entrySet().removeIf(entry -> {
String key = entry.getKey();
Long timestamp = cacheTimestamps.get("device_" + key);
return timestamp != null && currentTime - timestamp > DEVICE_CACHE_TIME;
});
// 清理过期的时间戳记录
cacheTimestamps.entrySet().removeIf(entry -> {
String key = entry.getKey();
Long timestamp = entry.getValue();
if (key.equals("token")) {
return currentTime - timestamp > TOKEN_EXPIRE_TIME;
} else if (key.startsWith("device_")) {
return currentTime - timestamp > DEVICE_CACHE_TIME;
}
return false;
});
}
/**
* 获取当前令牌
*/
private String getCurrentToken() {
return (String) tokenCache.get("currentToken");
}
/**
* 获取当前用户ID
*/
private String getCurrentUserId() {
return (String) tokenCache.get("currentUserId");
}
/**
* 保存令牌信息
*/
private void saveTokenInfo(String token, String userId) {
tokenCache.put("currentToken", token);
tokenCache.put("currentUserId", userId);
cacheTimestamps.put("token", System.currentTimeMillis());
}
/**
* 清除令牌信息
*/
private void clearTokenInfo() {
tokenCache.remove("currentToken");
tokenCache.remove("currentUserId");
cacheTimestamps.remove("token");
}
/**
* 第1步用户登录接口
* @param loginName 登录账号可选默认使用配置的账号
* @param loginPassword 登录密码可选默认使用配置的密码
* @param loginType 登录类型默认ENTERPRISE
* @param language 语言类型默认cn
* @param apply 应用类型默认APP
* @param isMd5 MD5验证默认0
* @return 登录结果
*/
@GetMapping("/login")
public ResponseEntity<Map<String, Object>> loginSystem(
@RequestParam(required = false) String loginName,
@RequestParam(required = false) String loginPassword,
@RequestParam(defaultValue = "ENTERPRISE") String loginType,
@RequestParam(defaultValue = "cn") String language,
@RequestParam(defaultValue = "APP") String apply,
@RequestParam(defaultValue = "0") String isMd5) {
// 使用传入的参数或默认常量
String actualLoginName = loginName != null ? loginName : DEFAULT_LOGIN_NAME;
String actualLoginPassword = loginPassword != null ? loginPassword : DEFAULT_LOGIN_PASSWORD;
try {
// 构建请求URL
String url = UriComponentsBuilder.fromHttpUrl("http://jltapp.168bds.com/GetDateServices.asmx/loginSystem")
.queryParam("LoginName", actualLoginName)
.queryParam("LoginPassword", actualLoginPassword)
.queryParam("LoginType", loginType)
.queryParam("language", language)
.queryParam("apply", apply)
.queryParam("ISMD5", isMd5)
.toUriString();
// 发送GET请求使用String类型接收
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
String responseBody = response.getBody();
if (responseBody != null) {
// 手动解析JSON
Map<String, Object> jsonResponse = objectMapper.readValue(responseBody,
new TypeReference<Map<String, Object>>() {});
// 检查登录是否成功
if ("true".equals(jsonResponse.get("success"))) {
// 保存令牌和用户ID到内存缓存
String token = (String) jsonResponse.get("mds");
String userId = (String) jsonResponse.get("id");
saveTokenInfo(token, userId);
return ResponseEntity.ok(jsonResponse);
} else {
return ResponseEntity.badRequest().body(jsonResponse);
}
} else {
return ResponseEntity.badRequest().body(createMapWithObjects(
"success", "false",
"msg", "响应为空",
"errorCode", 400
));
}
} catch (Exception e) {
Map<String, Object> errorMap = new HashMap<>();
errorMap.put("success", "false");
errorMap.put("msg", "登录失败:" + e.getMessage());
errorMap.put("errorCode", 500);
return ResponseEntity.internalServerError().body(errorMap);
}
}
/**
* 获取当前令牌信息
* @return 当前令牌
*/
@GetMapping("/current-token")
public ResponseEntity<Map<String, Object>> getTokenInfo() {
String currentToken = getCurrentToken();
String currentUserId = getCurrentUserId();
if (currentToken != null && currentUserId != null) {
return ResponseEntity.ok(createMapWithObjects(
"token", currentToken,
"userId", currentUserId,
"msg", "令牌获取成功"
));
} else {
return ResponseEntity.badRequest().body(createMapWithObjects(
"msg", "请先登录获取令牌",
"errorCode", 401
));
}
}
/**
* 第2步查询设备信息接口带缓存
* @param deviceId 设备ID可选如果不提供则查询所有预设设备
* @param useCache 是否使用缓存默认true
* @return 设备信息
*/
@GetMapping("/devices")
public ResponseEntity<Map<String, Object>> getDeviceInfo(
@RequestParam(required = false) String deviceId,
@RequestParam(defaultValue = "true") boolean useCache) {
String currentToken = getCurrentToken();
String currentUserId = getCurrentUserId();
// 检查是否已登录
if (currentToken == null || currentUserId == null) {
return ResponseEntity.badRequest().body(createMapWithObjects(
"success", "false",
"msg", "请先登录获取令牌",
"errorCode", 401
));
}
try {
java.util.List<Map<String, Object>> allDevices = new java.util.ArrayList<>();
// 如果指定了设备ID只查询该设备否则查询所有预设设备
String[] deviceIdsToQuery = deviceId != null ? new String[]{deviceId} : DEVICE_IDS;
for (String id : deviceIdsToQuery) {
// 检查缓存
if (useCache) {
Object cachedDevice = deviceCache.get(id);
Long cacheTime = cacheTimestamps.get("device_" + id);
if (cachedDevice != null && cacheTime != null && System.currentTimeMillis() - cacheTime < DEVICE_CACHE_TIME) {
if (cachedDevice instanceof java.util.List) {
allDevices.addAll((java.util.List<Map<String, Object>>) cachedDevice);
}
continue;
}
}
// 构建请求URL
String url = UriComponentsBuilder.fromHttpUrl("http://jltapp.168bds.com/GetDateServices.asmx/GetDate")
.queryParam("method", "loadUser")
.queryParam("user_id", id)
.queryParam("mds", currentToken)
.toUriString();
try {
// 发送GET请求使用String类型接收
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
String responseBody = response.getBody();
if (responseBody != null) {
// 手动解析JSON
Map<String, Object> jsonResponse = objectMapper.readValue(responseBody,
new TypeReference<Map<String, Object>>() {});
if ("true".equals(jsonResponse.get("success"))) {
// 获取设备数据
Object data = jsonResponse.get("data");
if (data instanceof java.util.List) {
java.util.List<Map<String, Object>> deviceList =
(java.util.List<Map<String, Object>>) data;
allDevices.addAll(deviceList);
// 缓存结果
if (useCache) {
deviceCache.put(id, deviceList);
cacheTimestamps.put("device_" + id, System.currentTimeMillis());
}
}
} else {
// 如果某个设备查询失败记录错误但继续查询其他设备
System.err.println("设备 " + id + " 查询失败: " + jsonResponse);
}
}
} catch (Exception e) {
System.err.println("设备 " + id + " 查询异常: " + e.getMessage());
// 继续查询其他设备
}
}
// 构建结果
Map<String, Object> result = createMapWithObjects(
"success", "true",
"errorCode", "200",
"msg", "设备信息查询成功",
"data", allDevices,
"total", allDevices.size(),
"fromCache", useCache
);
return ResponseEntity.ok(result);
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(createMapWithObjects(
"success", "false",
"msg", "设备信息查询失败:" + e.getMessage(),
"errorCode", 500
));
}
}
/**
* 查询单个设备详细信息
* @param deviceId 设备ID
* @return 设备详细信息
*/
@GetMapping("/device/{deviceId}")
public ResponseEntity<Map<String, Object>> getSingleDeviceInfo(@PathVariable String deviceId) {
String currentToken = getCurrentToken();
String currentUserId = getCurrentUserId();
// 检查是否已登录
if (currentToken == null || currentUserId == null) {
return ResponseEntity.badRequest().body(createMapWithObjects(
"success", "false",
"msg", "请先登录获取令牌",
"errorCode", 401
));
}
try {
// 构建请求URL
String url = UriComponentsBuilder.fromHttpUrl("http://jltapp.168bds.com/GetDateServices.asmx/GetDate")
.queryParam("method", "loadUser")
.queryParam("user_id", deviceId)
.queryParam("mds", currentToken)
.toUriString();
// 发送GET请求使用String类型接收
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
String responseBody = response.getBody();
if (responseBody != null) {
// 手动解析JSON
Map<String, Object> jsonResponse = objectMapper.readValue(responseBody,
new TypeReference<Map<String, Object>>() {});
if ("true".equals(jsonResponse.get("success"))) {
return ResponseEntity.ok(jsonResponse);
} else {
return ResponseEntity.badRequest().body(jsonResponse);
}
} else {
return ResponseEntity.badRequest().body(createMapWithObjects(
"success", "false",
"msg", "设备查询失败,响应为空",
"errorCode", 400
));
}
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(createMapWithObjects(
"success", "false",
"msg", "设备信息查询失败:" + e.getMessage(),
"errorCode", 500
));
}
}
/**
* 获取设备ID列表配置
* @return 当前配置的设备ID列表
*/
@GetMapping("/device-ids")
public ResponseEntity<Map<String, Object>> getDeviceIds() {
return ResponseEntity.ok(createMapWithObjects(
"success", "true",
"msg", "设备ID列表获取成功",
"deviceIds", java.util.Arrays.asList(DEVICE_IDS),
"total", DEVICE_IDS.length
));
}
/**
* 清除设备缓存
* @param deviceId 设备ID可选不提供则清除所有缓存
* @return 清除结果
*/
@GetMapping("/clear-cache")
public ResponseEntity<Map<String, Object>> clearDeviceCache(@RequestParam(required = false) String deviceId) {
if (deviceId != null) {
// 清除指定设备缓存
deviceCache.remove(deviceId);
cacheTimestamps.remove("device_" + deviceId);
} else {
// 清除所有设备缓存
deviceCache.clear();
cacheTimestamps.entrySet().removeIf(entry -> entry.getKey().startsWith("device_"));
}
return ResponseEntity.ok(createMapWithObjects(
"success", "true",
"msg", "缓存清除成功",
"errorCode", 200
));
}
/**
* 第3步令牌刷新接口
* @return 刷新结果
*/
@GetMapping("/refresh-token")
public ResponseEntity<Map<String, Object>> refreshToken() {
String currentToken = getCurrentToken();
// 检查是否已登录
if (currentToken == null) {
return ResponseEntity.badRequest().body(createMapWithObjects(
"success", "false",
"msg", "请先登录获取令牌",
"errorCode", 401
));
}
try {
// 构建请求URL
String url = UriComponentsBuilder.fromHttpUrl("http://jltapp.168bds.com/Command.aspx")
.queryParam("method", "RefreshMds")
.queryParam("mds", currentToken)
.toUriString();
// 发送GET请求刷新令牌
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
// 令牌刷新接口无数据返回只要请求成功即表示刷新成功
if (response.getStatusCode().is2xxSuccessful()) {
// 更新令牌时间戳
cacheTimestamps.put("token", System.currentTimeMillis());
return ResponseEntity.ok(createMapWithObjects(
"success", "true",
"msg", "令牌刷新成功",
"errorCode", 200,
"token", currentToken
));
} else {
return ResponseEntity.badRequest().body(createMapWithObjects(
"success", "false",
"msg", "令牌刷新失败",
"errorCode", response.getStatusCode().value()
));
}
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(createMapWithObjects(
"success", "false",
"msg", "令牌刷新失败:" + e.getMessage(),
"errorCode", 500
));
}
}
/**
* 登出接口 - 清除令牌和缓存
* @return 登出结果
*/
@GetMapping("/logout")
public ResponseEntity<Map<String, Object>> logout() {
clearTokenInfo();
deviceCache.clear();
cacheTimestamps.entrySet().removeIf(entry -> entry.getKey().startsWith("device_"));
return ResponseEntity.ok(createMapWithObjects(
"success", "true",
"msg", "登出成功",
"errorCode", 200
));
}
/**
* 获取缓存统计信息
* @return 缓存统计
*/
@GetMapping("/cache-stats")
public ResponseEntity<Map<String, Object>> getCacheStats() {
return ResponseEntity.ok(createMapWithObjects(
"success", "true",
"msg", "缓存统计获取成功",
"tokenCacheSize", tokenCache.size(),
"deviceCacheSize", deviceCache.size(),
"timestampCacheSize", cacheTimestamps.size(),
"hasValidToken", getCurrentToken() != null
));
}
/**
* 定时刷新令牌建议每分钟调用一次
* 可以配合定时任务使用
* @return 刷新结果
*/
@GetMapping("/auto-refresh")
public ResponseEntity<Map<String, Object>> autoRefreshToken() {
String currentToken = getCurrentToken();
if (currentToken == null) {
return ResponseEntity.ok(createMapWithObjects(
"success", "true",
"msg", "无需刷新,当前未登录",
"refreshed", false
));
}
return refreshToken();
}
/**
* 获取所有设备定位信息
* @param mapType 地图类型默认BAIDU
* @return 设备定位信息列表
*/
@GetMapping("/locations")
public ResponseEntity<Map<String, Object>> getDeviceLocations(@RequestParam(defaultValue = "BAIDU",required = false) String mapType) {
String currentToken = getCurrentToken();
String currentUserId = getCurrentUserId();
// 检查是否已登录
if (currentToken == null || currentUserId == null) {
return ResponseEntity.badRequest().body(createMapWithObjects(
"success", "false",
"msg", "请先登录获取令牌",
"errorCode", 401
));
}
try {
// 构建请求URL
String url = UriComponentsBuilder.fromHttpUrl("http://jltapp.168bds.com/GetDateServices.asmx/GetDate")
.queryParam("method", "getDeviceListByCustomId")
.queryParam("mds", currentToken)
.queryParam("id", currentUserId)
.queryParam("mapType", mapType)
.toUriString();
// 发送GET请求
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
String responseBody = response.getBody();
if (responseBody != null) {
// 手动解析JSON
Map<String, Object> jsonResponse = objectMapper.readValue(responseBody,
new TypeReference<Map<String, Object>>() {});
if ("true".equals(jsonResponse.get("success"))) {
// 处理返回的数据
List<Map<String, Object>> data = (List<Map<String, Object>>) jsonResponse.get("data");
if (data != null && !data.isEmpty()) {
Map<String, Object> deviceData = data.get(0);
Map<String, Integer> keyMap = (Map<String, Integer>) deviceData.get("key");
List<List<Object>> records = (List<List<Object>>) deviceData.get("records");
// 转换数据格式
List<Map<String, Object>> formattedDevices = new ArrayList<>();
for (List<Object> record : records) {
Map<String, Object> device = new HashMap<>();
for (Map.Entry<String, Integer> entry : keyMap.entrySet()) {
device.put(entry.getKey(), record.get(entry.getValue()));
}
formattedDevices.add(device);
}
// 构建返回结果
return ResponseEntity.ok(createMapWithObjects(
"success", "true",
"msg", "设备定位信息获取成功",
"errorCode", 200,
"total", formattedDevices.size(),
"devices", formattedDevices
));
}
}
return ResponseEntity.ok(jsonResponse);
} else {
return ResponseEntity.badRequest().body(createMapWithObjects(
"success", "false",
"msg", "响应为空",
"errorCode", 400
));
}
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(createMapWithObjects(
"success", "false",
"msg", "获取设备定位信息失败:" + e.getMessage(),
"errorCode", 500
));
}
}
/**
* 创建Map的辅助方法Java 8兼容
*/
private Map<String, Object> createMap(String... keyValues) {
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < keyValues.length; i += 2) {
if (i + 1 < keyValues.length) {
map.put(keyValues[i], keyValues[i + 1]);
}
}
return map;
}
private Map<String, Object> createMapWithObjects(Object... keyValues) {
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < keyValues.length; i += 2) {
if (i + 1 < keyValues.length) {
map.put((String) keyValues[i], keyValues[i + 1]);
}
}
return map;
}
}

View File

@ -0,0 +1,263 @@
package com.bonus.material.iot.controller;
import com.bonus.common.core.web.domain.AjaxResult;
import com.bonus.material.iot.dto.GpsData;
import com.bonus.material.iot.dto.PhaseData;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author : syruan
* @version : 1.0
* @CreateTime: 2025-04-02 13:51
* @Description: 贵州iot设备GPS解析接口
*/
@RestController
public class IotDataController {
// 使用线程安全的Map临时存储最新数据
private static final ConcurrentMap<String, GpsData> latestData = new ConcurrentHashMap<>();
// 接收MQTT数据的接口
@PostMapping("/api/mqtt/data")
public String processMqttData(@RequestBody String mqttData) {
try {
GpsData gpsData = parseMqttData(mqttData);
// 存储最新数据key可以用设备UUID
latestData.put(gpsData.getUuid(), gpsData);
return "数据接收并存储成功";
} catch (Exception e) {
return "数据解析失败: " + e.getMessage();
}
}
// 新增接口供HTML页面获取数据
@GetMapping("/api/mqtt/latest-data")
public AjaxResult getLatestData(@RequestParam(required = false) String uuid) {
if (uuid == null && !latestData.isEmpty()) {
// 如果没有指定UUID返回第一个设备的数据
GpsData data = latestData.values().iterator().next();
return AjaxResult.success(data);
}
return AjaxResult.warn("", latestData.get(uuid));
}
private GpsData parseMqttData(String rawData) {
GpsData data = new GpsData();
try {
// 1. 解析UUID
int uuidStart = rawData.indexOf("uuid:") + 5;
int uuidEnd = rawData.indexOf("/", uuidStart);
data.setUuid(rawData.substring(uuidStart, uuidEnd));
// 2. 解析$GNRMC部分
int gnrmcStart = rawData.indexOf("$GNRMC:") + 7;
int gnrmcEnd = rawData.indexOf("$GNGGA:", gnrmcStart);
String gnrmc = rawData.substring(gnrmcStart, gnrmcEnd);
// 处理校验和
String[] gnrmcMainAndChecksum = gnrmc.split("\\*");
String checksum = gnrmcMainAndChecksum.length > 1 ? gnrmcMainAndChecksum[1] : "";
data.setChecksumRMC(checksum);
// 分割主数据部分
String[] gnrmcParts = gnrmcMainAndChecksum[0].split(":", -1);
// 安全设置字段
if(gnrmcParts.length > 0) {
data.setUtcTime(gnrmcParts[0]);
}
if(gnrmcParts.length > 1) {
data.setStatus(gnrmcParts[1]);
}
if(gnrmcParts.length > 2) {
data.setLatitude(gnrmcParts[2]);
}
if(gnrmcParts.length > 3) {
data.setLatDirection(gnrmcParts[3]);
}
if(gnrmcParts.length > 4) {
data.setLongitude(gnrmcParts[4]);
}
if(gnrmcParts.length > 5) {
data.setLonDirection(gnrmcParts[5]);
}
if(gnrmcParts.length > 6) {
data.setSpeedOverGround(gnrmcParts[6]);
}
if(gnrmcParts.length > 7) {
data.setCourseOverGround(gnrmcParts[7]);
}
if(gnrmcParts.length > 8) {
data.setDate(gnrmcParts[8]);
}
if(gnrmcParts.length > 11) {
data.setModeIndicator(gnrmcParts[11]);
}
if(gnrmcParts.length > 12) {
data.setNavStatus(gnrmcParts[12]);
}
// 3. 解析$GNGGA部分
int gnggaStart = gnrmcEnd + 7;
int gnggaEnd = rawData.indexOf("/humi_temp:", gnggaStart);
String gngga = rawData.substring(gnggaStart, gnggaEnd);
String[] gnggaParts = gngga.split(":", -1);
if(gnggaParts.length > 0) {
data.setUtcTime(gnggaParts[0]);
}
if(gnggaParts.length > 1) {
data.setLatitude(gnggaParts[1]);
}
if(gnggaParts.length > 2) {
data.setLatDirection(gnggaParts[2]);
}
if(gnggaParts.length > 3) {
data.setLongitude(gnggaParts[3]);
}
if(gnggaParts.length > 4) {
data.setLonDirection(gnggaParts[4]);
}
if(gnggaParts.length > 5) {
data.setQuality(gnggaParts[5]);
}
if(gnggaParts.length > 6) {
data.setNumSatellitesUsed(gnggaParts[6]);
}
if(gnggaParts.length > 7) {
data.setHdop(gnggaParts[7]);
}
if(gnggaParts.length > 8) {
data.setAltitude(gnggaParts[8]);
}
if(gnggaParts.length > 10) {
data.setGeoidSeparation(gnggaParts[10]);
}
// 4. 解析humi_temp
int humiTempStart = gnggaEnd + 11;
int humiTempEnd = rawData.indexOf(":/acc_gry:", humiTempStart);
String humiTemp = rawData.substring(humiTempStart, humiTempEnd);
String[] humiTempParts = humiTemp.split(":");
if(humiTempParts.length > 0) {
data.setHumidity(humiTempParts[0]);
}
if(humiTempParts.length > 1) {
data.setTemperature(humiTempParts[1]);
}
// 5. 解析acc_gry
int accGryStart = humiTempEnd + 10;
int accGryEnd = rawData.length();
// 假设是最后一部分
if(rawData.indexOf(":/", accGryStart) > 0) {
accGryEnd = rawData.indexOf(":/", accGryStart);
}
String accGry = rawData.substring(accGryStart, accGryEnd);
String[] accGryParts = accGry.split(":");
if(accGryParts.length > 0) {
data.setAccX(accGryParts[0]);
}
if(accGryParts.length > 1) {
data.setAccY(accGryParts[1]);
}
if(accGryParts.length > 2) {
data.setAccZ(accGryParts[2]);
}
if(accGryParts.length > 3) {
data.setGyroX(accGryParts[3]);
}
if(accGryParts.length > 4) {
data.setGyroY(accGryParts[4]);
}
if(accGryParts.length > 5) {
data.setGyroZ(accGryParts[5]);
}
// 解析A相电数据
int aPhaseStart = rawData.indexOf("/A_PHEASE:") + 10;
int aPhaseEnd = rawData.indexOf(":/B_PHEASE:", aPhaseStart);
if (aPhaseStart > 0 && aPhaseEnd > aPhaseStart) {
String aPhaseStr = rawData.substring(aPhaseStart, aPhaseEnd);
data.setAPhase(parsePhaseData(aPhaseStr));
}
// 解析B相电数据
int bPhaseStart = rawData.indexOf("/B_PHEASE:") + 10;
int bPhaseEnd = rawData.indexOf(":/C_PHEASE:", bPhaseStart);
if (bPhaseStart > 0 && bPhaseEnd > bPhaseStart) {
String bPhaseStr = rawData.substring(bPhaseStart, bPhaseEnd);
data.setBPhase(parsePhaseData(bPhaseStr));
}
// 解析C相电数据
int cPhaseStart = rawData.indexOf("/C_PHEASE:") + 10;
int cPhaseEnd = rawData.indexOf(":/", cPhaseStart);
if (cPhaseStart > 0 && cPhaseEnd > cPhaseStart) {
String cPhaseStr = rawData.substring(cPhaseStart, cPhaseEnd);
data.setCPhase(parsePhaseData(cPhaseStr));
}
// 验证三相数据
validatePhaseData(data.getAPhase());
validatePhaseData(data.getBPhase());
validatePhaseData(data.getCPhase());
} catch (Exception e) {
System.err.println("解析过程中发生错误: " + e.getMessage());
throw new RuntimeException("数据解析失败: " + e.getMessage());
}
return data;
}
// 验证三项电数据
private void validatePhaseData(PhaseData phase) {
if (phase == null) {
return;
}
// 验证电压范围
if (phase.getVoltage() != null) {
double voltage = Double.parseDouble(phase.getVoltage());
if (voltage < 0 || voltage > 300) {
throw new IllegalArgumentException("电压值超出合理范围");
}
}
// 可以添加更多验证规则...
// 频率验证功率因数验证等
}
// 解析三项电数据
private PhaseData parsePhaseData(String phaseStr) {
String[] parts = phaseStr.split(":");
if (parts.length < 14) {
return null;
}
PhaseData phase = new PhaseData();
phase.setVoltage(parts[0]);
phase.setCurrent(parts[1]);
phase.setActivePower(parts[2]);
phase.setReactivePower(parts[3]);
phase.setPowerFactor(parts[4]);
phase.setFrequency(parts[5]);
phase.setForwardActiveEnergy(parts[6]);
phase.setForwardReactiveEnergy(parts[7]);
phase.setReverseActiveEnergy(parts[8]);
phase.setReverseReactiveEnergy(parts[9]);
phase.setApparentPower(parts[10]);
phase.setHarmonicActivePower(parts[11]);
phase.setFundamentalActivePower(parts[12]);
phase.setFundamentalReactivePower(parts[13]);
return phase;
}
}

View File

@ -0,0 +1,53 @@
package com.bonus.material.iot.dto;
/**
* @author : 阮世耀
* @version : 1.0
* @PackagePath: com.tencent.wxcloudrun.model.dto
* @CreateTime: 2025-04-02 13:50
* @Description: iot定位设备上报信息
*/
import lombok.Data;
@Data
public class GpsData {
// UUID部分
private String uuid;
// $GNRMC部分
private String utcTime;
private String status;
private String latitude;
private String latDirection;
private String longitude;
private String lonDirection;
private String speedOverGround;
private String courseOverGround;
private String date;
private String modeIndicator;
private String navStatus;
private String checksumRMC;
// $GNGGA部分
private String quality;
private String numSatellitesUsed;
private String hdop;
private String altitude;
private String geoidSeparation;
// 环境数据
private String humidity;
private String temperature;
// 加速度和陀螺仪数据
private String accX;
private String accY;
private String accZ;
private String gyroX;
private String gyroY;
private String gyroZ;
// 新增三相电数据
private PhaseData aPhase;
private PhaseData bPhase;
private PhaseData cPhase;
}

View File

@ -0,0 +1,30 @@
package com.bonus.material.iot.dto;
import lombok.Data;
/**
* @author : 阮世耀
* @version : 1.0
* @PackagePath: com.tencent.wxcloudrun.model.dto
* @CreateTime: 2025-04-03 15:47
* @Description: iot定位设备的三相电压数据
* @ModifyUser: 阮世耀
*
*/
@Data
public class PhaseData {
private String voltage; // 电压(V)
private String current; // 电流(A)
private String activePower; // 有功功率(W)
private String reactivePower; // 无功功率(VAR)
private String powerFactor; // 功率因素
private String frequency; // 频率(Hz)
private String forwardActiveEnergy; // 正向有功电度(KWh)
private String forwardReactiveEnergy; // 正向无功电度(kvar·h)
private String reverseActiveEnergy; // 反向有功电度(KWh)
private String reverseReactiveEnergy; // 反向无功电度(kvar·h)
private String apparentPower; // 视在功率(VA)
private String harmonicActivePower; // 谐波有功功率(W)
private String fundamentalActivePower; // 基波有功功率(W)
private String fundamentalReactivePower; // 基波无功功率(VAR)
}