车辆位置,极值,位置等重要数据数据
This commit is contained in:
parent
b29f7cfb81
commit
0de7503010
11
pom.xml
11
pom.xml
@ -50,6 +50,12 @@
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot 测试 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -270,6 +276,11 @@
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@ -114,7 +114,8 @@ public class SecurityConfig
|
||||
requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
|
||||
// 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
|
||||
.antMatchers("/swagger-ui.html", "/swagger-ui/**", "/swagger-resources/**", "/webjars/**", "/v3/api-docs/**", "/*/api-docs", "/druid/**", "/doc.html", "/doc.html/**", "/knife4j/**").permitAll()
|
||||
.antMatchers("/dev-api/swagger-ui/**", "/dev-api/swagger-resources/**", "/dev-api/v3/api-docs/**", "/dev-api/doc.html", "/dev-api/knife4j/**").permitAll()
|
||||
.antMatchers("/devices/**","/ota/**").permitAll()
|
||||
.antMatchers("/battery-data/**").permitAll()
|
||||
.antMatchers("/vehicle-data/**").permitAll()
|
||||
|
||||
@ -54,11 +54,7 @@ public class SwaggerConfig
|
||||
.apiInfo(apiInfo())
|
||||
// 设置哪些接口暴露给Swagger展示
|
||||
.select()
|
||||
// 扫描所有有注解的api,用这种方式更灵活
|
||||
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
|
||||
// 扫描指定包中的swagger注解
|
||||
// .apis(RequestHandlerSelectors.basePackage("com.evobms.project.tool.swagger"))
|
||||
// 扫描所有 .apis(RequestHandlerSelectors.any())
|
||||
.apis(RequestHandlerSelectors.basePackage("com.evobms.project"))
|
||||
.paths(PathSelectors.any())
|
||||
.build()
|
||||
/* 设置安全模式,swagger可以设置访问token */
|
||||
|
||||
@ -2,6 +2,7 @@ package com.evobms.project.Battery.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.evobms.framework.aspectj.lang.annotation.Excel;
|
||||
@ -13,6 +14,7 @@ import com.evobms.framework.web.domain.BaseEntity;
|
||||
* @author 田志阳
|
||||
* @date 2025-11-15
|
||||
*/
|
||||
@Data
|
||||
public class SubsystemTemperature extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -48,115 +50,11 @@ public class SubsystemTemperature extends BaseEntity
|
||||
/** 创建时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date createdAt;
|
||||
private Date createTime;
|
||||
|
||||
/** 更新时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date updatedAt;
|
||||
private Date updateTime;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId)
|
||||
{
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceId()
|
||||
{
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setTimestamp(Date timestamp)
|
||||
{
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Date getTimestamp()
|
||||
{
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setSubsystemCount(Integer subsystemCount)
|
||||
{
|
||||
this.subsystemCount = subsystemCount;
|
||||
}
|
||||
|
||||
public Integer getSubsystemCount()
|
||||
{
|
||||
return subsystemCount;
|
||||
}
|
||||
|
||||
public void setSubsystemNo(Integer subsystemNo)
|
||||
{
|
||||
this.subsystemNo = subsystemNo;
|
||||
}
|
||||
|
||||
public Integer getSubsystemNo()
|
||||
{
|
||||
return subsystemNo;
|
||||
}
|
||||
|
||||
public void setTempProbeCount(Integer tempProbeCount)
|
||||
{
|
||||
this.tempProbeCount = tempProbeCount;
|
||||
}
|
||||
|
||||
public Integer getTempProbeCount()
|
||||
{
|
||||
return tempProbeCount;
|
||||
}
|
||||
|
||||
public void setTemperatureValues(String temperatureValues)
|
||||
{
|
||||
this.temperatureValues = temperatureValues;
|
||||
}
|
||||
|
||||
public String getTemperatureValues()
|
||||
{
|
||||
return temperatureValues;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Date createdAt)
|
||||
{
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public Date getCreatedAt()
|
||||
{
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(Date updatedAt)
|
||||
{
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public Date getUpdatedAt()
|
||||
{
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("timestamp", getTimestamp())
|
||||
.append("subsystemCount", getSubsystemCount())
|
||||
.append("subsystemNo", getSubsystemNo())
|
||||
.append("tempProbeCount", getTempProbeCount())
|
||||
.append("temperatureValues", getTemperatureValues())
|
||||
.append("createdAt", getCreatedAt())
|
||||
.append("updatedAt", getUpdatedAt())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,8 @@ import com.evobms.project.Battery.service.IExtremeValuesService;
|
||||
import com.evobms.project.Battery.domain.ExtremeValues;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
/**
|
||||
* 图标组件Controller
|
||||
*
|
||||
@ -28,6 +30,7 @@ import java.text.SimpleDateFormat;
|
||||
* @date 2025-11-15
|
||||
*/
|
||||
@RestController
|
||||
@Api(tags = "图表组件")
|
||||
public class BatteryChartController {
|
||||
|
||||
@Autowired
|
||||
@ -47,6 +50,7 @@ public class BatteryChartController {
|
||||
|
||||
// 图表数据:按最近 hours 小时返回整车电压/电流/SOC 的时间序列
|
||||
@GetMapping("/battery-data/{deviceId}/chart")
|
||||
@ApiOperation(value = "整车电压电流SOC时间序列", notes = "按最近hours小时返回时间序列")
|
||||
public List<ChartDataPoint> getChartData(@PathVariable("deviceId") String deviceId, @RequestParam(value = "hours", required = false, defaultValue = "24") int hours) {
|
||||
long millis = System.currentTimeMillis() - (long) hours * 3600_000L;
|
||||
Date startTime = new Date(millis);
|
||||
@ -61,18 +65,21 @@ public class BatteryChartController {
|
||||
|
||||
// 整车最新:返回 deviceId 最新的一条整车数据
|
||||
@GetMapping("/vehicle-data/{deviceId}/latest")
|
||||
@ApiOperation(value = "整车最新数据", notes = "返回指定设备最新整车数据")
|
||||
public VehicleData latestVehicleData(@PathVariable("deviceId") String deviceId) {
|
||||
return vehicleDataService.selectLatestByDeviceId(deviceId);
|
||||
}
|
||||
|
||||
// 位置最新:返回 deviceId 最新的一条位置数据
|
||||
@GetMapping("/vehicle-location/{deviceId}/latest")
|
||||
@ApiOperation(value = "车辆最新位置", notes = "返回指定设备最新位置数据")
|
||||
public VehicleLocation latestVehicleLocation(@PathVariable("deviceId") String deviceId) {
|
||||
return vehicleLocationService.selectLatestByDeviceId(deviceId);
|
||||
}
|
||||
|
||||
// 位置轨迹:按时间范围返回位置列表,start/end 为毫秒时间戳,可选 limit 限制条数
|
||||
@GetMapping("/vehicle-location/{deviceId}")
|
||||
@ApiOperation(value = "车辆位置轨迹", notes = "按时间范围返回位置列表")
|
||||
public List<VehicleLocation> locationRange(@PathVariable("deviceId") String deviceId,
|
||||
@RequestParam("start") long startMillis,
|
||||
@RequestParam("end") long endMillis,
|
||||
@ -84,25 +91,28 @@ public class BatteryChartController {
|
||||
|
||||
// 子系统最新帧(电压):返回指定子系统号最新的一帧电压数据
|
||||
@GetMapping("/subsystem-voltage/{deviceId}/{subsystemNo}/latest")
|
||||
@ApiOperation(value = "子系统最新电压帧", notes = "返回指定子系统最新电压数据")
|
||||
public SubsystemVoltage latestVoltage(@PathVariable("deviceId") String deviceId, @PathVariable("subsystemNo") Integer subsystemNo) {
|
||||
return subsystemVoltageService.selectLatestByDeviceAndSubsystem(deviceId, subsystemNo);
|
||||
}
|
||||
|
||||
// 子系统最新帧(温度):返回指定子系统号最新的一帧温度数据
|
||||
@GetMapping("/subsystem-temperature/{deviceId}/{subsystemNo}/latest")
|
||||
@ApiOperation(value = "子系统最新温度帧", notes = "返回指定子系统最新温度数据")
|
||||
public SubsystemTemperature latestTemperature(@PathVariable("deviceId") String deviceId, @PathVariable("subsystemNo") Integer subsystemNo) {
|
||||
return subsystemTemperatureService.selectLatestByDeviceAndSubsystem(deviceId, subsystemNo);
|
||||
}
|
||||
|
||||
@GetMapping("/extreme-values/{deviceId}/latest")
|
||||
@ApiOperation(value = "极值最新数据", notes = "返回指定设备极值数据,支持before筛选")
|
||||
public ExtremeValues latestExtremeValues(@PathVariable("deviceId") String deviceId,
|
||||
@RequestParam(value = "before", required = false) String before) {
|
||||
if (before == null || before.isEmpty()) {
|
||||
return extremeValuesService.selectLatestByDeviceId(deviceId);
|
||||
}
|
||||
java.util.Date beforeTime = null;
|
||||
Date beforeTime = null;
|
||||
if (before.matches("\\d+")) {
|
||||
beforeTime = new java.util.Date(Long.parseLong(before));
|
||||
beforeTime = new Date(Long.parseLong(before));
|
||||
} else {
|
||||
try {
|
||||
beforeTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(before);
|
||||
|
||||
@ -26,8 +26,8 @@ public class VehicleData extends BaseEntity
|
||||
private String deviceId;
|
||||
|
||||
/** 数据时间戳 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date timestamp;
|
||||
|
||||
/** 车辆状态 (1:启动 2:熄火 3:其他 254:异常 255:无效) */
|
||||
|
||||
@ -2,6 +2,7 @@ package com.evobms.project.VehicleData.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
@ -14,99 +15,97 @@ import com.evobms.framework.web.domain.BaseEntity;
|
||||
* @author ruoyi
|
||||
* @date 2025-11-15
|
||||
*/
|
||||
public class VehicleLocation extends BaseEntity
|
||||
{
|
||||
public class VehicleLocation extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键ID */
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/** 设备ID */
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
@Excel(name = "设备ID")
|
||||
private String deviceId;
|
||||
|
||||
/** 数据时间戳 */
|
||||
/**
|
||||
* 数据时间戳
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date timestamp;
|
||||
|
||||
/** 定位状态 (0:无效 1:有效) */
|
||||
/**
|
||||
* 定位状态 (0:无效 1:有效)
|
||||
*/
|
||||
@Excel(name = "定位状态 (0:无效 1:有效)")
|
||||
private Integer positioningStatus;
|
||||
|
||||
/** 经度 */
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
@Excel(name = "经度")
|
||||
private BigDecimal longitude;
|
||||
|
||||
/** 纬度 */
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
@Excel(name = "纬度")
|
||||
private BigDecimal latitude;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId)
|
||||
{
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceId()
|
||||
{
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setTimestamp(Date timestamp)
|
||||
{
|
||||
public void setTimestamp(Date timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Date getTimestamp()
|
||||
{
|
||||
public Date getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setPositioningStatus(Integer positioningStatus)
|
||||
{
|
||||
public void setPositioningStatus(Integer positioningStatus) {
|
||||
this.positioningStatus = positioningStatus;
|
||||
}
|
||||
|
||||
public Integer getPositioningStatus()
|
||||
{
|
||||
public Integer getPositioningStatus() {
|
||||
return positioningStatus;
|
||||
}
|
||||
|
||||
public void setLongitude(BigDecimal longitude)
|
||||
{
|
||||
public void setLongitude(BigDecimal longitude) {
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public BigDecimal getLongitude()
|
||||
{
|
||||
public BigDecimal getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public void setLatitude(BigDecimal latitude)
|
||||
{
|
||||
public void setLatitude(BigDecimal latitude) {
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
public BigDecimal getLatitude()
|
||||
{
|
||||
public BigDecimal getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("timestamp", getTimestamp())
|
||||
|
||||
@ -5,17 +5,28 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
public class GBT32960FullDecoder {
|
||||
private static final Logger log = LoggerFactory.getLogger(GBT32960FullDecoder.class);
|
||||
// 从SN码生成AES密钥
|
||||
private static byte[] generateKeyFromSn(String sn) {
|
||||
StringBuilder keyStr = new StringBuilder(sn);
|
||||
while (keyStr.length() < 16) {
|
||||
keyStr.append("0");
|
||||
String s = sn == null ? "" : sn;
|
||||
String key;
|
||||
if ("EVO0001".equals(s)) {
|
||||
StringBuilder sb = new StringBuilder(16);
|
||||
sb.append("EVO0001");
|
||||
while (sb.length() < 16) sb.append('0');
|
||||
key = sb.toString();
|
||||
} else if (s.length() >= 16) {
|
||||
key = s.substring(s.length() - 16);
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder(16);
|
||||
sb.append(s);
|
||||
while (sb.length() < 16) sb.append('0');
|
||||
key = sb.toString();
|
||||
}
|
||||
keyStr = new StringBuilder(keyStr.substring(0, 16));
|
||||
return keyStr.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
return key.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
// AES解密
|
||||
@ -173,6 +184,7 @@ public class GBT32960FullDecoder {
|
||||
|
||||
//使用的AES密钥
|
||||
byte[] aesKey = generateKeyFromSn(deviceSn);
|
||||
System.out.println("密钥------------------------>"+aesKey);
|
||||
byte[] decryptedData = decryptData(encryptedData, aesKey);
|
||||
bytesData = bytesToHex(decryptedData);
|
||||
log.info("解密成功!");
|
||||
|
||||
@ -30,6 +30,7 @@ import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.evobms.project.system.service.IBmsDevicesService;
|
||||
import com.evobms.project.system.domain.BmsDevices;
|
||||
@ -73,7 +74,6 @@ public class MqttService {
|
||||
private IChargeDischargeSummaryService chargeDischargeSummaryService;
|
||||
|
||||
|
||||
|
||||
@Value("${mqtt.publishTopic}")
|
||||
private String publishTopic;
|
||||
|
||||
@ -220,6 +220,7 @@ public class MqttService {
|
||||
log.error("处理设备数据时发生错误: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 逐字段解析数据字符串 - 直接解析十六进制数据
|
||||
*
|
||||
@ -252,31 +253,35 @@ public class MqttService {
|
||||
log.error("设备 {} 解析数据字段时发生错误: {}", deviceSn, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照协议定义的索引位置解析各个数据段
|
||||
*/
|
||||
private void parseByProtocolIndex(byte[] message, String deviceSn) {
|
||||
try {
|
||||
// 1. 时间信息 - 索引24-29 (6字节)
|
||||
parseTimeAtIndex(message, 0, deviceSn);
|
||||
Date frameTime = parseTimeAtIndex(message, 0, deviceSn);
|
||||
if (frameTime == null) {
|
||||
frameTime = new Date();
|
||||
}
|
||||
|
||||
// 2. 整车数据 - 索引30-49 (20字节)
|
||||
parseVehicleDataAtIndex(message, 6, deviceSn);
|
||||
parseVehicleDataAtIndex(message, 6, deviceSn, frameTime);
|
||||
|
||||
// 3. 位置信息 - 索引51-60 (10字节)
|
||||
parseLocationDataAtIndex(message, 27, deviceSn);
|
||||
parseLocationDataAtIndex(message, 27, deviceSn, frameTime);
|
||||
|
||||
// 4. 极值数据 - 索引61-75 (14字节)
|
||||
parseExtremeDataAtIndex(message, 37, deviceSn);
|
||||
parseExtremeDataAtIndex(message, 37, deviceSn, frameTime);
|
||||
|
||||
// 5. 单体电压数据 - 索引76-447 (372字节)
|
||||
parseCellVoltageAtIndex(message, 52, deviceSn);
|
||||
parseCellVoltageAtIndex(message, 52, deviceSn, frameTime);
|
||||
|
||||
// 6. 单体温度数据 - 索引448-542 (95字节)
|
||||
parseCellTemperatureAtIndex(message, 424, deviceSn);
|
||||
parseCellTemperatureAtIndex(message, 424, deviceSn, frameTime);
|
||||
|
||||
// 7. 累计充放电量 - 索引543-553 (11字节)
|
||||
parseChargeDischargeAtIndex(message, 519, deviceSn);
|
||||
parseChargeDischargeAtIndex(message, 519, deviceSn, frameTime);
|
||||
|
||||
log.info("设备 {} 所有数据段解析完成", deviceSn);
|
||||
|
||||
@ -284,15 +289,16 @@ public class MqttService {
|
||||
log.error("设备 {} 按索引解析时发生错误: {}", deviceSn, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析时间信息 - 索引24-29 (6字节)
|
||||
* 格式: 年(偏移2000) + 月 + 日 + 时 + 分 + 秒
|
||||
*/
|
||||
private void parseTimeAtIndex(byte[] message, int startIndex, String deviceSn) {
|
||||
private Date parseTimeAtIndex(byte[] message, int startIndex, String deviceSn) {
|
||||
try {
|
||||
if (message.length < startIndex + 6) {
|
||||
log.warn("设备 {}: 时间数据索引越界", deviceSn);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
int year = (message[startIndex] & 0xFF) + 2000; // 索引24: 年
|
||||
@ -307,19 +313,26 @@ public class MqttService {
|
||||
year, month, day, hour, minute, second);
|
||||
log.info("设备 {} - 时间: {}", deviceSn, time);
|
||||
saveTimeData(deviceSn, time);
|
||||
Calendar cal = Calendar.getInstance(TimeZone.getDefault());
|
||||
cal.set(year, month - 1, day, hour, minute, second);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
return cal.getTime();
|
||||
} else {
|
||||
log.info("设备 {} - 时间: 无效数据({}-{}-{} {}:{}:{})",
|
||||
deviceSn, year, month, day, hour, minute, second);
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("设备 {} 解析时间数据时发生错误: {}", deviceSn, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析整车数据 - 索引30-49 (20字节)
|
||||
*/
|
||||
private void parseVehicleDataAtIndex(byte[] message, int startIndex, String deviceSn) {
|
||||
private void parseVehicleDataAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) {
|
||||
try {
|
||||
if (message.length < startIndex + 20) {
|
||||
log.warn("设备 {}: 整车数据索引越界", deviceSn);
|
||||
@ -359,7 +372,7 @@ public class MqttService {
|
||||
log.info("设备 {} - 正极绝缘电阻: {} Ω", deviceSn, insulationResistance);
|
||||
|
||||
// 保存整车数据
|
||||
saveVehicleData(deviceSn, voltageValue, currentValue, soc, chargeStatus,insulationResistance);
|
||||
saveVehicleData(deviceSn, voltageValue, currentValue, soc, chargeStatus, insulationResistance, frameTime);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("设备 {} 解析整车数据时发生错误: {}", deviceSn, e.getMessage());
|
||||
@ -370,7 +383,7 @@ public class MqttService {
|
||||
/**
|
||||
* 解析位置信息 - 索引51-60 (10字节)
|
||||
*/
|
||||
private void parseLocationDataAtIndex(byte[] message, int startIndex, String deviceSn) {
|
||||
private void parseLocationDataAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) {
|
||||
try {
|
||||
if (message.length < startIndex + 10) {
|
||||
log.warn("设备 {}: 位置数据索引越界", deviceSn);
|
||||
@ -388,7 +401,8 @@ public class MqttService {
|
||||
|
||||
// 索引52: 位置状态
|
||||
byte locationStatus = message[startIndex + 1];
|
||||
|
||||
// 转成十六进制字符串,例如 "00", "01"
|
||||
String locationStatusHex = String.format("%02X", locationStatus);
|
||||
// 索引53-56: 经度 (0.000001°)
|
||||
long longitude = ((message[startIndex + 2] & 0xFFL) << 24) |
|
||||
((message[startIndex + 3] & 0xFFL) << 16) |
|
||||
@ -404,8 +418,9 @@ public class MqttService {
|
||||
if (longitude != 0 || latitude != 0) {
|
||||
double lon = longitude / 1000000.0;
|
||||
double lat = latitude / 1000000.0;
|
||||
log.info("设备 {} - 位置: 经度={}°, 纬度={}° (状态:0x{})", deviceSn, lon, lat, String.format("%02X", locationStatus));
|
||||
processLocationData(deviceSn, lon, lat,String.format("%02X", locationStatus));
|
||||
|
||||
log.info("设备 {} - 位置: 经度={}°, 纬度={}° (状态:0x{})", deviceSn, lon, lat, locationStatusHex);
|
||||
processLocationData(deviceSn, lon, lat, locationStatusHex, frameTime);
|
||||
} else {
|
||||
log.info("设备 {} - 位置: 无效数据", deviceSn);
|
||||
}
|
||||
@ -418,7 +433,7 @@ public class MqttService {
|
||||
/**
|
||||
* 解析极值数据 - 索引61-75 (14字节)
|
||||
*/
|
||||
private void parseExtremeDataAtIndex(byte[] message, int startIndex, String deviceSn) {
|
||||
private void parseExtremeDataAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) {
|
||||
try {
|
||||
if (message.length < startIndex + 14) {
|
||||
log.warn("设备 {}: 极值数据索引越界", deviceSn);
|
||||
@ -468,7 +483,7 @@ public class MqttService {
|
||||
int minTempValue = minTemperature - 40;
|
||||
log.info("设备 {} - 最低温度: 子系统{}-探针{}, {}°C", deviceSn, minTempSystem, minTempProbe, minTempValue);
|
||||
// 保存极值数据
|
||||
saveExtremeData(deviceSn, (maxVoltageSystem & 0xFF), (maxVoltageProbe & 0xFF), maxVoltageValue, (minVoltageSystem & 0xFF), (minVoltageProbe & 0xFF), minVoltageValue, (maxTempSystem & 0xFF), (maxTempProbe & 0xFF), maxTempValue, (minTempSystem & 0xFF), (minTempProbe & 0xFF), minTempValue);
|
||||
saveExtremeData(deviceSn, (maxVoltageSystem & 0xFF), (maxVoltageProbe & 0xFF), maxVoltageValue, (minVoltageSystem & 0xFF), (minVoltageProbe & 0xFF), minVoltageValue, (maxTempSystem & 0xFF), (maxTempProbe & 0xFF), maxTempValue, (minTempSystem & 0xFF), (minTempProbe & 0xFF), minTempValue, frameTime);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("设备 {} 解析极值数据时发生错误: {}", deviceSn, e.getMessage());
|
||||
@ -479,7 +494,7 @@ public class MqttService {
|
||||
/**
|
||||
* 解析单体电压数据 - 索引76-447 (372字节)
|
||||
*/
|
||||
private void parseCellVoltageAtIndex(byte[] message, int startIndex, String deviceSn) {
|
||||
private void parseCellVoltageAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) {
|
||||
try {
|
||||
if (message.length < startIndex + 372) {
|
||||
log.warn("设备 {}: 单体电压数据索引越界", deviceSn);
|
||||
@ -535,7 +550,7 @@ public class MqttService {
|
||||
}
|
||||
SubsystemVoltage sv = new SubsystemVoltage();
|
||||
sv.setDeviceId(deviceSn);
|
||||
sv.setTimestamp(new Date());
|
||||
sv.setTimestamp(frameTime);
|
||||
sv.setSubsystemCount((int) subsystemCount);
|
||||
sv.setSubsystemNo((int) subsystemNum);
|
||||
sv.setSubsystemVoltage(BigDecimal.valueOf(storageVoltageValue));
|
||||
@ -546,7 +561,7 @@ public class MqttService {
|
||||
sv.setCreateTime(new Date());
|
||||
sv.setCreateBy(deviceSn);
|
||||
String voltagesStr = voltages.stream().map(Object::toString).collect(java.util.stream.Collectors.joining(","));
|
||||
sv.setBatteryVoltages(voltagesStr);
|
||||
sv.setBatteryVoltages(JSON.toJSONString(voltagesStr));
|
||||
subsystemVoltageService.insertSubsystemVoltage(sv);
|
||||
parseCellVoltagesDetail(message, voltageDataStart, deviceSn, startCellIndex, frameCellCount);
|
||||
} else {
|
||||
@ -562,7 +577,7 @@ public class MqttService {
|
||||
/**
|
||||
* 解析单体温度数据 - 索引448-542 (95字节)
|
||||
*/
|
||||
private void parseCellTemperatureAtIndex(byte[] message, int startIndex, String deviceSn) {
|
||||
private void parseCellTemperatureAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) {
|
||||
try {
|
||||
if (message.length < startIndex + 89) {
|
||||
log.warn("设备 {}: 单体温度数据索引越界", deviceSn);
|
||||
@ -603,14 +618,14 @@ public class MqttService {
|
||||
}
|
||||
SubsystemTemperature st = new SubsystemTemperature();
|
||||
st.setDeviceId(deviceSn);
|
||||
st.setTimestamp(new Date());
|
||||
st.setTimestamp(frameTime);
|
||||
st.setSubsystemCount((int) subsystemCount);
|
||||
st.setSubsystemNo((int) subsystemNum);
|
||||
st.setTempProbeCount(totalTempCount);
|
||||
st.setCreateTime(new Date());
|
||||
st.setCreateBy(deviceSn);
|
||||
String tempsStr = temperatures.stream().map(Object::toString).collect(java.util.stream.Collectors.joining(","));
|
||||
st.setTemperatureValues(tempsStr);
|
||||
String tempsStr = temperatures.stream().map(Object::toString).collect(Collectors.joining(","));
|
||||
st.setTemperatureValues(JSON.toJSONString(tempsStr));
|
||||
subsystemTemperatureService.insertSubsystemTemperature(st);
|
||||
parseCellTemperaturesDetail(message, tempDataStart, deviceSn, totalTempCount);
|
||||
} else {
|
||||
@ -625,7 +640,7 @@ public class MqttService {
|
||||
/**
|
||||
* 解析累计充放电量 - 索引543-553 (11字节)
|
||||
*/
|
||||
private void parseChargeDischargeAtIndex(byte[] message, int startIndex, String deviceSn) {
|
||||
private void parseChargeDischargeAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) {
|
||||
try {
|
||||
if (message.length < startIndex + 11) {
|
||||
log.warn("设备 {}: 充放电数据索引越界", deviceSn);
|
||||
@ -659,7 +674,7 @@ public class MqttService {
|
||||
|
||||
log.info("设备 {} - 累计充放电量: 放电{}kWh, 充电{}kWh", deviceSn, dischargeKwh, chargeKwh);
|
||||
|
||||
saveChargeDischargeSummary(deviceSn, dischargeKwh, chargeKwh);
|
||||
saveChargeDischargeSummary(deviceSn, dischargeKwh, chargeKwh, frameTime);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("设备 {} 解析充放电数据时发生错误: {}", deviceSn, e.getMessage());
|
||||
@ -667,8 +682,6 @@ public class MqttService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 解析详细的单体温度数据
|
||||
*/
|
||||
@ -734,6 +747,7 @@ public class MqttService {
|
||||
|
||||
log.info("设备 {} - 电压统计: 最小值{}V, 最大值{}V, 平均值{}V", deviceSn, minVoltage, maxVoltage, avgVoltage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查单体电压数据有效性
|
||||
*/
|
||||
@ -746,18 +760,25 @@ public class MqttService {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 充电状态描述
|
||||
*/
|
||||
private int getChargeStatusDescription(byte status) {
|
||||
/** 充电状态 (1:停车充电 2:行驶充电 3:未充电 4:充电完成 254:异常 255:无效) */
|
||||
switch (status & 0xFF) {
|
||||
case 0x01: return 1;
|
||||
case 0x02: return 2;
|
||||
case 0x03: return 3;
|
||||
case 0x04: return 4;
|
||||
case 0xFE: return 254;
|
||||
case 0xFF: return 255;
|
||||
case 0x01:
|
||||
return 1;
|
||||
case 0x02:
|
||||
return 2;
|
||||
case 0x03:
|
||||
return 3;
|
||||
case 0x04:
|
||||
return 4;
|
||||
case 0xFE:
|
||||
return 254;
|
||||
case 0xFF:
|
||||
return 255;
|
||||
|
||||
}
|
||||
return 0;
|
||||
@ -774,6 +795,7 @@ public class MqttService {
|
||||
minute >= 0 && minute <= 59 &&
|
||||
second >= 0 && second <= 59;
|
||||
}
|
||||
|
||||
/**
|
||||
* 十六进制字符串转字节数组
|
||||
*/
|
||||
@ -793,8 +815,7 @@ public class MqttService {
|
||||
*/
|
||||
private void saveCellVoltageData(String deviceSn, byte subsystemNum, int startIndex, int count, List<Double> voltages, double min, double max, double avg) {
|
||||
// TODO: 实现单体电压数据保存逻辑
|
||||
log.debug("保存设备 {} 子系统 {} 电压数据: 起始{}, 数量{}, 范围{}-{}V",
|
||||
deviceSn, subsystemNum, startIndex, count, min, max);
|
||||
log.debug("保存设备 {} 子系统 {} 电压数据: 起始{}, 数量{}, 范围{}-{}V", deviceSn, subsystemNum, startIndex, count, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -802,8 +823,7 @@ public class MqttService {
|
||||
*/
|
||||
private void saveCellTemperatureData(String deviceSn, byte subsystemNum, int count, List<Integer> temperatures, int min, int max, double avg) {
|
||||
// TODO: 实现单体温度数据保存逻辑
|
||||
log.debug("保存设备 {} 子系统 {} 温度数据: 数量{}, 范围{}-{}°C",
|
||||
deviceSn, subsystemNum, count, min, max);
|
||||
log.debug("保存设备 {} 子系统 {} 温度数据: 数量{}, 范围{}-{}°C", deviceSn, subsystemNum, count, min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -813,13 +833,15 @@ public class MqttService {
|
||||
// TODO: 实现时间数据保存逻辑
|
||||
log.debug("保存设备 {} 时间数据: {}", deviceSn, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存整车数据
|
||||
*/
|
||||
private void saveVehicleData(String deviceSn, double voltage, double current, int soc, byte chargeStatus,int insulationResistance) {
|
||||
private void saveVehicleData(String deviceSn, double voltage, double current, int soc, byte chargeStatus,int insulationResistance, Date frameTime) {
|
||||
// 保存整车数据到数据库
|
||||
VehicleData ve = new VehicleData();
|
||||
ve.setDeviceId(deviceSn);
|
||||
ve.setTimestamp(frameTime);
|
||||
ve.setTotalVoltage(BigDecimal.valueOf(voltage));
|
||||
ve.setTotalCurrent(BigDecimal.valueOf(current));
|
||||
ve.setSoc(soc);
|
||||
@ -831,11 +853,13 @@ public class MqttService {
|
||||
}
|
||||
|
||||
|
||||
private void processLocationData(String deviceSn, double longitude, double latitude,String locationStatus) {
|
||||
private void processLocationData(String deviceSn, double longitude, double latitude,String locationStatus, Date frameTime) {
|
||||
VehicleLocation ve = new VehicleLocation();
|
||||
ve.setDeviceId(deviceSn);
|
||||
ve.setTimestamp(new Date());
|
||||
ve.setPositioningStatus(Integer.valueOf(String.format("%02X", locationStatus)));
|
||||
ve.setTimestamp(frameTime);
|
||||
// 十六进制字符串(如 "00")转成整数(如 0)
|
||||
int status = Integer.parseInt(locationStatus, 16);
|
||||
ve.setPositioningStatus(status);
|
||||
ve.setLongitude(BigDecimal.valueOf(longitude));
|
||||
ve.setLatitude(BigDecimal.valueOf(latitude));
|
||||
ve.setCreateTime(new Date());
|
||||
@ -844,10 +868,10 @@ public class MqttService {
|
||||
}
|
||||
|
||||
private void saveExtremeData(String deviceSn, int maxVoltageSubsystemNo, int maxVoltageBatteryNo, double maxVoltageValue, int minVoltageSubsystemNo, int minVoltageBatteryNo, double minVoltageValue, int maxTempSubsystemNo,
|
||||
int maxTempProbeNo, int maxTempValue, int minTempSubsystemNo, int minTempProbeNo, int minTempValue) {
|
||||
int maxTempProbeNo, int maxTempValue, int minTempSubsystemNo, int minTempProbeNo, int minTempValue, Date frameTime) {
|
||||
ExtremeValues ex = new ExtremeValues();
|
||||
ex.setDeviceId(deviceSn);
|
||||
ex.setTimestamp(new Date());
|
||||
ex.setTimestamp(frameTime);
|
||||
ex.setMaxVoltageSubsystemNo(maxVoltageSubsystemNo);
|
||||
ex.setMaxVoltageBatteryNo(maxVoltageBatteryNo);
|
||||
ex.setMaxVoltageValue(BigDecimal.valueOf(maxVoltageValue));
|
||||
@ -863,16 +887,17 @@ public class MqttService {
|
||||
extremeValuesService.insertExtremeValues(ex);
|
||||
}
|
||||
|
||||
private void saveChargeDischargeSummary(String deviceSn, double dischargeKwh, double chargeKwh) {
|
||||
private void saveChargeDischargeSummary(String deviceSn, double dischargeKwh, double chargeKwh, Date frameTime) {
|
||||
ChargeDischargeSummary s = new ChargeDischargeSummary();
|
||||
s.setDeviceId(deviceSn);
|
||||
s.setTimestamp(new Date());
|
||||
s.setTimestamp(frameTime);
|
||||
s.setDischargeKwh(BigDecimal.valueOf(dischargeKwh));
|
||||
s.setChargeKwh(BigDecimal.valueOf(chargeKwh));
|
||||
s.setCreateTime(new Date());
|
||||
s.setCreateBy(deviceSn);
|
||||
chargeDischargeSummaryService.insertChargeDischargeSummary(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单体温度数据
|
||||
*/
|
||||
@ -888,18 +913,6 @@ public class MqttService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 更新设备数据
|
||||
*
|
||||
@ -933,8 +946,6 @@ public class MqttService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据设备唯一编号(deviceCode)查询系统设备表的SN码
|
||||
*/
|
||||
|
||||
@ -9,6 +9,10 @@ import com.evobms.project.iot.service.DeviceTokenService;
|
||||
import com.evobms.project.system.domain.BmsDevices;
|
||||
import com.evobms.project.system.service.IBmsDevicesService;
|
||||
import com.evobms.project.ota.service.IOtaTasksService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.MDC;
|
||||
@ -25,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
@ -46,6 +51,7 @@ import java.util.Map;
|
||||
* 路径:POST /iot/auth/token
|
||||
*/
|
||||
@RestController
|
||||
@Tag(name = "设备鉴权", description = "设备鉴权令牌接口")
|
||||
@RequestMapping("/iot")
|
||||
public class IotAuthController {
|
||||
private static final Logger log = LoggerFactory.getLogger(IotAuthController.class);
|
||||
@ -89,6 +95,7 @@ public class IotAuthController {
|
||||
* 返回:{ code, success, token }
|
||||
*/
|
||||
@PostMapping("/auth/token")
|
||||
@Operation(summary = "设备请求令牌", description = "通过SN码,设备号,版本信息生成TOKEN")
|
||||
public Map<String, Object> requestToken(@RequestBody Map<String, String> body) {
|
||||
String sn = safeTrim(body.get("sn"));
|
||||
String model = safeTrim(body.get("device"));
|
||||
@ -133,7 +140,7 @@ public class IotAuthController {
|
||||
return resp;
|
||||
}
|
||||
|
||||
// 设备合法,改为自定义非JWT令牌,长度固定65
|
||||
// 设备合法,改为JWT令牌,长度固定65
|
||||
String token = deviceTokenService.issueTokenForSn(sn);
|
||||
log.info("BBOX设备token: {}", token);
|
||||
log.info("设备令牌签发成功: sn={} model={} version={} tokenLen={}", sn, model, version, token != null ? token.length() : 0);
|
||||
@ -162,6 +169,8 @@ public class IotAuthController {
|
||||
* 请求参数:version(可选)
|
||||
* 返回:{ code, success, data(Base64) }
|
||||
*/
|
||||
@Operation(summary = " MQTT 登录", description = "获取设备端 MQTT 登录信息")
|
||||
@ApiOperation(value = "MQTT 登录", notes = "获取设备端 MQTT 登录信息")
|
||||
@GetMapping("/auth/mqtt")
|
||||
public Map<String, Object> getMqttCredentials(@RequestParam(value = "version", required = false) String version,
|
||||
HttpServletRequest request) {
|
||||
@ -186,10 +195,11 @@ public class IotAuthController {
|
||||
if (uri.getPort() != -1) {
|
||||
port = uri.getPort();
|
||||
}
|
||||
} catch (Exception ignore) {}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
|
||||
String use = fixLengthRight(mqttUsername, 8);
|
||||
String pass = fixLengthRight(mqttPassword,16);
|
||||
String pass = fixLengthRight(mqttPassword, 16);
|
||||
|
||||
Map<String, String> data = new HashMap<>();
|
||||
data.put("clientid", sn);
|
||||
@ -199,10 +209,17 @@ public class IotAuthController {
|
||||
data.put("passwd", pass);
|
||||
|
||||
String json = JSON.toJSONString(data);
|
||||
log.debug("登录mqtt信息====》{}",json);
|
||||
String key16 = fixLengthRight(sn, 16); // AES密钥:sn后补0到16位
|
||||
String encryptedBase64 = AesEcbPkcs5Utils.encryptToAscII16(json, key16);
|
||||
log.debug("登录mqtt信息加密===================》{}",encryptedBase64);
|
||||
log.debug("登录mqtt信息====》{}", json);
|
||||
// AES密钥:sn后补0到16位
|
||||
String encryptedBase64;
|
||||
if (sn.equals("EVO0001")) {
|
||||
String key161 = fixLengthRight("EVO0001", 16);
|
||||
encryptedBase64 = AesEcbPkcs5Utils.encryptToAscII16(json, key161);
|
||||
} else {
|
||||
String keyTail6 = sn.length() >= 16 ? sn.substring(sn.length() - 16) : sn;
|
||||
encryptedBase64 = AesEcbPkcs5Utils.encryptToAscII16(json, keyTail6);
|
||||
}
|
||||
log.debug("登录mqtt信息加密===================》{}", encryptedBase64);
|
||||
resp.put("code", BboxApiConstants.CODE_SUCCESS);
|
||||
resp.put("success", "1");
|
||||
resp.put("data", encryptedBase64);
|
||||
@ -221,6 +238,7 @@ public class IotAuthController {
|
||||
* 请求参数(body):{ "version":"1.0.0" }
|
||||
* 返回:{ code(3位), success(1位), update(1位), version(5位), url(11位), count(3位) }
|
||||
*/
|
||||
@ApiOperation(value = "设备登录", notes = "设备登录并上报固件版本,服务端判断是否需要OTA升级")
|
||||
@PostMapping("/auth/login")
|
||||
public Map<String, Object> deviceLogin(@RequestBody(required = false) String body, @RequestParam(value = "version", required = false) String versionParam, HttpServletRequest request) {
|
||||
Map<String, Object> resp = new HashMap<>();
|
||||
@ -285,7 +303,8 @@ public class IotAuthController {
|
||||
|
||||
// 目标升级版本:直接取设备记录中的固件版本(跳过 OTA 任务查询)
|
||||
String targetVersion = null;
|
||||
try { targetVersion = device.getFirmwareVersion();
|
||||
try {
|
||||
targetVersion = device.getFirmwareVersion();
|
||||
} catch (Exception ignore) {
|
||||
|
||||
}
|
||||
@ -369,6 +388,7 @@ public class IotAuthController {
|
||||
* - 后续分片:与首片相同,每片前置相同模式的校验头(摘要基于该分片的数据),随后紧跟该分片数据
|
||||
*/
|
||||
@GetMapping("/ota/{num}")
|
||||
@Operation(summary = "OTA固件分片下载", description = " OTA 固件分片下载接口")
|
||||
public Object downloadOtaChunk(@PathVariable("num") String num,
|
||||
@RequestParam(value = "version", required = false) String version,
|
||||
HttpServletRequest request) {
|
||||
@ -453,7 +473,8 @@ public class IotAuthController {
|
||||
log.info("OTA首片头HEX({}): {}", headerLen, toHex(header));
|
||||
int prefixLen = Math.min(64, firstData.length);
|
||||
log.info("OTA首片数据HEX前{}字节: {}", prefixLen, toHex(Arrays.copyOfRange(firstData, 0, prefixLen)));
|
||||
} catch (Exception ignore) {}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
} else {
|
||||
// 后续分片:从首片实际数据(1024)之后开始切片;并在分片前置校验头(摘要基于该分片数据)
|
||||
int firstDataLen = Math.min(otaChunkSize, firmwareBytes);
|
||||
@ -486,7 +507,8 @@ public class IotAuthController {
|
||||
log.info("OTA分片头HEX({}): {}", headerLen, toHex(header));
|
||||
int prefixLen = Math.min(64, dataForDigest.length);
|
||||
log.info("OTA分片数据HEX前{}字节: {}", prefixLen, toHex(Arrays.copyOfRange(dataForDigest, 0, prefixLen)));
|
||||
} catch (Exception ignore) {}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
log.debug("OTA分片发送: sn={} version={} num={} chunkLen={}",
|
||||
sn, version, num, chunk.length);
|
||||
@ -504,7 +526,7 @@ public class IotAuthController {
|
||||
} finally {
|
||||
MDC.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态计算 OTA 分片总数
|
||||
@ -554,7 +576,8 @@ public class IotAuthController {
|
||||
String rel = base + version + ".bin";
|
||||
Resource res = new ClassPathResource(rel);
|
||||
if (!res.exists()) return null;
|
||||
try (InputStream in = res.getInputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
|
||||
try (InputStream in = res.getInputStream();
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
|
||||
byte[] buf = new byte[4096];
|
||||
int r;
|
||||
while ((r = in.read(buf)) != -1) bos.write(buf, 0, r);
|
||||
@ -580,7 +603,7 @@ public class IotAuthController {
|
||||
private static String sha256Hex(byte[] data) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] digest = md.digest(data);
|
||||
StringBuilder sb = new StringBuilder(digest.length );
|
||||
StringBuilder sb = new StringBuilder(digest.length);
|
||||
for (byte b : digest) {
|
||||
//转为 16 进制字符串
|
||||
String s = Integer.toHexString(0xff & b);
|
||||
@ -608,7 +631,9 @@ public class IotAuthController {
|
||||
|
||||
private static String fixLengthRight(String s, int len) {
|
||||
if (s == null) s = "";
|
||||
if (s.length() >= len) return s.substring(0, len);
|
||||
if (s.length() >= len){
|
||||
return s.substring(0, len);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
sb.append(s);
|
||||
while (sb.length() < len) sb.append('0');
|
||||
|
||||
@ -47,7 +47,7 @@ public class BmsDevices extends BaseEntity
|
||||
@Excel(name = "设备IP地址")
|
||||
private String ipAddress;
|
||||
|
||||
/** 固件版本号 */
|
||||
/** 服务器固件版本号 */
|
||||
@Excel(name = "固件版本号")
|
||||
private String firmwareVersion;
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ ruoyi:
|
||||
version: 3.9.0
|
||||
# 版权年份
|
||||
copyrightYear: 2025
|
||||
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
|
||||
# 文件路径
|
||||
profile: D:/ruoyi/uploadPath
|
||||
# 获取ip地址开关
|
||||
addressEnabled: false
|
||||
@ -15,8 +15,8 @@ ruoyi:
|
||||
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 8080
|
||||
# 服务器HTTP端口,支持环境变量覆盖:SERVER_PORT,默认8080
|
||||
port: ${SERVER_PORT:8081}
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
@ -208,11 +208,11 @@ mqtt:
|
||||
# 默认QoS等级
|
||||
defaultQos: 1
|
||||
# 订阅主题
|
||||
subscribeTopic: /bbox/EVO0002/#
|
||||
subscribeTopic: "/bbox/+/#"
|
||||
# 发布主题
|
||||
publishTopic: evobms/command
|
||||
|
||||
# 调试:开启HTTP请求打印
|
||||
debug:
|
||||
debug:
|
||||
httpdump:
|
||||
enabled: true
|
||||
|
||||
@ -13,13 +13,13 @@
|
||||
<result property="tempProbeCount" column="temp_probe_count" />
|
||||
<result property="temperatureValues" column="temperature_values" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updatedTime" column="updated_time" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSubsystemTemperatureVo">
|
||||
select id, device_id, timestamp, subsystem_count, subsystem_no, temp_probe_count, temperature_values, create_time, updated_time, create_by, update_by from subsystem_temperature
|
||||
select id, device_id, timestamp, subsystem_count, subsystem_no, temp_probe_count, temperature_values, create_time, update_time, create_by, update_by from subsystem_temperature
|
||||
</sql>
|
||||
|
||||
<select id="selectSubsystemTemperatureList" parameterType="SubsystemTemperature" resultMap="SubsystemTemperatureResult">
|
||||
@ -31,7 +31,7 @@
|
||||
<if test="subsystemNo != null "> and subsystem_no = #{subsystemNo}</if>
|
||||
<if test="tempProbeCount != null "> and temp_probe_count = #{tempProbeCount}</if>
|
||||
<if test="temperatureValues != null and temperatureValues != ''"> and temperature_values = #{temperatureValues}</if>
|
||||
<if test="updatedTime != null "> and updated_time = #{updatedTime}</if>
|
||||
<if test="updateTime != null "> and update_time = #{updateTime}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
<if test="tempProbeCount != null">temp_probe_count,</if>
|
||||
<if test="temperatureValues != null">temperature_values,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updatedTime != null">updated_time,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
</trim>
|
||||
@ -62,7 +62,7 @@
|
||||
<if test="tempProbeCount != null">#{tempProbeCount},</if>
|
||||
<if test="temperatureValues != null">#{temperatureValues},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updatedTime != null">#{updatedTime},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
</trim>
|
||||
@ -78,7 +78,7 @@
|
||||
<if test="tempProbeCount != null">temp_probe_count = #{tempProbeCount},</if>
|
||||
<if test="temperatureValues != null">temperature_values = #{temperatureValues},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="updatedTime != null">updated_time = #{updatedTime},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
</trim>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user