From 6b6be61716a3e68bb8a2ac97b13b30490dd9ca51 Mon Sep 17 00:00:00 2001 From: tzy Date: Mon, 23 Feb 2026 08:44:44 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BD=A6=E8=BE=86=E4=BD=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E6=9E=81=E5=80=BC=EF=BC=8C=E4=BD=8D=E7=BD=AE=E7=AD=89=E9=87=8D?= =?UTF-8?q?=E8=A6=81=E6=95=B0=E6=8D=AE=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vite/deps/package.json | 3 + .../evobms/framework/config/MqttConfig.java | 1 - .../framework/config/SwaggerConfig.java | 6 +- .../framework/web/domain/BaseEntity.java | 8 + .../controller/ExtremeValuesController.java | 27 +- .../SubsystemTemperatureController.java | 25 +- .../SubsystemVoltageController.java | 25 +- .../project/battery/domain/ExtremeValues.java | 22 ++ .../battery/domain/SubsystemTemperature.java | 14 + .../battery/domain/SubsystemVoltage.java | 16 + .../bms/controller/GBT32960FullDecoder.java | 342 +++++++++++------- .../bms/controller/MqttPublishController.java | 9 + .../bms/controller/MqttTestController.java | 5 + .../project/bms/domain/BmsChargeDetail.java | 75 ++++ .../evobms/project/bms/domain/BmsSopData.java | 67 ++++ .../project/bms/domain/BmsSystemStatus.java | 59 +++ .../bms/mapper/BmsChargeDetailMapper.java | 9 + .../project/bms/mapper/BmsSopDataMapper.java | 9 + .../bms/mapper/BmsSystemStatusMapper.java | 9 + .../bms/service/IBmsChargeDetailService.java | 7 + .../bms/service/IBmsSopDataService.java | 7 + .../bms/service/IBmsSystemStatusService.java | 7 + .../project/bms/service/MqttService.java | 167 +++++++-- .../impl/BmsChargeDetailServiceImpl.java | 18 + .../service/impl/BmsSopDataServiceImpl.java | 18 + .../impl/BmsSystemStatusServiceImpl.java | 18 + .../iot/controller/IotAuthController.java | 8 +- .../iot/controller/IotOtaController.java | 4 + .../ota/controller/OtaTasksController.java | 9 + .../evobms/project/ota/domain/OtaTasks.java | 125 +------ .../ChargeDischargeSummaryController.java | 11 +- .../domain/ChargeDischargeSummary.java | 77 +--- .../controller/BmsDevicesController.java | 19 +- .../project/system/domain/BmsDevices.java | 12 + .../controller/BatteryChartController.java | 30 +- .../controller/VehicleDataController.java | 11 +- .../controller/VehicleLocationController.java | 11 +- .../vehicledata/domain/HomeDashboardDTO.java | 12 +- .../vehicledata/domain/VehicleData.java | 193 ++-------- .../vehicledata/domain/VehicleLocation.java | 74 +--- src/main/resources/application-druid.yml | 2 +- src/main/resources/application.yml | 2 +- src/main/resources/banner.txt | 32 +- .../mybatis/bms/BmsChargeDetailMapper.xml | 71 ++++ .../mybatis/bms/BmsSopDataMapper.xml | 65 ++++ .../mybatis/bms/BmsSystemStatusMapper.xml | 59 +++ .../resources/ota/{0.0.1.bin => 0.0.6.bin} | Bin 124828 -> 123996 bytes src/main/resources/ota/1.0.2.bin | 1 - src/main/resources/ota/2.2.1.bin | Bin 124756 -> 0 bytes 49 files changed, 1180 insertions(+), 621 deletions(-) create mode 100644 .vite/deps/package.json create mode 100644 src/main/java/com/evobms/project/bms/domain/BmsChargeDetail.java create mode 100644 src/main/java/com/evobms/project/bms/domain/BmsSopData.java create mode 100644 src/main/java/com/evobms/project/bms/domain/BmsSystemStatus.java create mode 100644 src/main/java/com/evobms/project/bms/mapper/BmsChargeDetailMapper.java create mode 100644 src/main/java/com/evobms/project/bms/mapper/BmsSopDataMapper.java create mode 100644 src/main/java/com/evobms/project/bms/mapper/BmsSystemStatusMapper.java create mode 100644 src/main/java/com/evobms/project/bms/service/IBmsChargeDetailService.java create mode 100644 src/main/java/com/evobms/project/bms/service/IBmsSopDataService.java create mode 100644 src/main/java/com/evobms/project/bms/service/IBmsSystemStatusService.java create mode 100644 src/main/java/com/evobms/project/bms/service/impl/BmsChargeDetailServiceImpl.java create mode 100644 src/main/java/com/evobms/project/bms/service/impl/BmsSopDataServiceImpl.java create mode 100644 src/main/java/com/evobms/project/bms/service/impl/BmsSystemStatusServiceImpl.java create mode 100644 src/main/resources/mybatis/bms/BmsChargeDetailMapper.xml create mode 100644 src/main/resources/mybatis/bms/BmsSopDataMapper.xml create mode 100644 src/main/resources/mybatis/bms/BmsSystemStatusMapper.xml rename src/main/resources/ota/{0.0.1.bin => 0.0.6.bin} (68%) delete mode 100644 src/main/resources/ota/1.0.2.bin delete mode 100644 src/main/resources/ota/2.2.1.bin diff --git a/.vite/deps/package.json b/.vite/deps/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/.vite/deps/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/src/main/java/com/evobms/framework/config/MqttConfig.java b/src/main/java/com/evobms/framework/config/MqttConfig.java index 29fb55d..b671597 100644 --- a/src/main/java/com/evobms/framework/config/MqttConfig.java +++ b/src/main/java/com/evobms/framework/config/MqttConfig.java @@ -150,7 +150,6 @@ public class MqttConfig { // 尽最大可能保留原始字节(ISO_8859_1一一映射) payloadBytes = ((String) payloadObj).getBytes(StandardCharsets.ISO_8859_1); } else { - // 兜底:从toString获取,再按ISO_8859_1转换 payloadBytes = payloadObj.toString().getBytes(StandardCharsets.ISO_8859_1); } diff --git a/src/main/java/com/evobms/framework/config/SwaggerConfig.java b/src/main/java/com/evobms/framework/config/SwaggerConfig.java index 0f31dbc..40e72cb 100644 --- a/src/main/java/com/evobms/framework/config/SwaggerConfig.java +++ b/src/main/java/com/evobms/framework/config/SwaggerConfig.java @@ -23,7 +23,7 @@ import springfox.documentation.spring.web.plugins.Docket; /** * Swagger2的接口配置 - * + * * @author ruoyi */ @Configuration @@ -108,9 +108,9 @@ public class SwaggerConfig // 用ApiInfoBuilder进行定制 return new ApiInfoBuilder() // 设置标题 - .title("标题:若依管理系统_接口文档") + .title("标题:EVO-BMS 电池管理系统_接口文档") // 描述 - .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...") + .description("") // 作者信息 .contact(new Contact(ruoyiConfig.getName(), null, null)) // 版本 diff --git a/src/main/java/com/evobms/framework/web/domain/BaseEntity.java b/src/main/java/com/evobms/framework/web/domain/BaseEntity.java index f0f5d92..07ca85d 100644 --- a/src/main/java/com/evobms/framework/web/domain/BaseEntity.java +++ b/src/main/java/com/evobms/framework/web/domain/BaseEntity.java @@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableField; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModelProperty; /** * Entity基类 @@ -21,28 +22,35 @@ public class BaseEntity implements Serializable /** 搜索值 */ @JsonIgnore @TableField(exist = false) + @ApiModelProperty(value = "搜索值") private String searchValue; /** 创建者 */ + @ApiModelProperty(value = "创建者") private String createBy; /** 创建时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "创建时间") private Date createTime; /** 更新者 */ + @ApiModelProperty(value = "更新者") private String updateBy; /** 更新时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "更新时间") private Date updateTime; /** 备注 */ @TableField(exist = false) + @ApiModelProperty(value = "备注") private String remark; /** 请求参数 */ @JsonInclude(JsonInclude.Include.NON_EMPTY) + @ApiModelProperty(value = "请求参数") @TableField(exist = false) private Map params; diff --git a/src/main/java/com/evobms/project/battery/controller/ExtremeValuesController.java b/src/main/java/com/evobms/project/battery/controller/ExtremeValuesController.java index 71c5d5b..ca6d553 100644 --- a/src/main/java/com/evobms/project/battery/controller/ExtremeValuesController.java +++ b/src/main/java/com/evobms/project/battery/controller/ExtremeValuesController.java @@ -20,15 +20,18 @@ import com.evobms.framework.web.controller.BaseController; import com.evobms.framework.web.domain.AjaxResult; import com.evobms.common.utils.poi.ExcelUtil; import com.evobms.framework.web.page.TableDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; /** * 电池极值数据Controller * * @author 田志阳 - * @date 2025-10-14 + * @date 2025-11-15 */ @RestController -@RequestMapping("/BatteryData/BatteryData") +@RequestMapping("/Battery/ExtremeValues") +@Api(tags = "电池极值数据") public class ExtremeValuesController extends BaseController { @Autowired @@ -37,8 +40,9 @@ public class ExtremeValuesController extends BaseController /** * 查询电池极值数据列表 */ - @PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:list')") + @PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:list')") @GetMapping("/list") + @ApiOperation("查询电池极值数据列表") public TableDataInfo list(ExtremeValues extremeValues) { startPage(); @@ -49,9 +53,10 @@ public class ExtremeValuesController extends BaseController /** * 导出电池极值数据列表 */ - @PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:export')") + @PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:export')") @Log(title = "电池极值数据", businessType = BusinessType.EXPORT) @PostMapping("/export") + @ApiOperation("导出电池极值数据列表") public void export(HttpServletResponse response, ExtremeValues extremeValues) { List list = extremeValuesService.selectExtremeValuesList(extremeValues); @@ -62,8 +67,9 @@ public class ExtremeValuesController extends BaseController /** * 获取电池极值数据详细信息 */ - @PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:query')") + @PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:query')") @GetMapping(value = "/{id}") + @ApiOperation("获取电池极值数据详细信息") public AjaxResult getInfo(@PathVariable("id") Long id) { return success(extremeValuesService.selectExtremeValuesById(id)); @@ -72,9 +78,10 @@ public class ExtremeValuesController extends BaseController /** * 新增电池极值数据 */ - @PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:add')") + @PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:add')") @Log(title = "电池极值数据", businessType = BusinessType.INSERT) @PostMapping + @ApiOperation("新增电池极值数据") public AjaxResult add(@RequestBody ExtremeValues extremeValues) { return toAjax(extremeValuesService.insertExtremeValues(extremeValues)); @@ -83,9 +90,10 @@ public class ExtremeValuesController extends BaseController /** * 修改电池极值数据 */ - @PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:edit')") + @PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:edit')") @Log(title = "电池极值数据", businessType = BusinessType.UPDATE) @PutMapping + @ApiOperation("修改电池极值数据") public AjaxResult edit(@RequestBody ExtremeValues extremeValues) { return toAjax(extremeValuesService.updateExtremeValues(extremeValues)); @@ -94,9 +102,10 @@ public class ExtremeValuesController extends BaseController /** * 删除电池极值数据 */ - @PreAuthorize("@ss.hasPermi('BatteryData:BatteryData:remove')") + @PreAuthorize("@ss.hasPermi('Battery:ExtremeValues:remove')") @Log(title = "电池极值数据", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") + @DeleteMapping("/{ids}") + @ApiOperation("删除电池极值数据") public AjaxResult remove(@PathVariable Long[] ids) { return toAjax(extremeValuesService.deleteExtremeValuesByIds(ids)); diff --git a/src/main/java/com/evobms/project/battery/controller/SubsystemTemperatureController.java b/src/main/java/com/evobms/project/battery/controller/SubsystemTemperatureController.java index fd230dc..c9eaf5d 100644 --- a/src/main/java/com/evobms/project/battery/controller/SubsystemTemperatureController.java +++ b/src/main/java/com/evobms/project/battery/controller/SubsystemTemperatureController.java @@ -20,6 +20,8 @@ import com.evobms.framework.web.controller.BaseController; import com.evobms.framework.web.domain.AjaxResult; import com.evobms.common.utils.poi.ExcelUtil; import com.evobms.framework.web.page.TableDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; /** * 可充电储能子系统温度信息Controller @@ -28,7 +30,8 @@ import com.evobms.framework.web.page.TableDataInfo; * @date 2025-11-15 */ @RestController -@RequestMapping("/temperature/temperature") +@RequestMapping("/Battery/SubsystemTemperature") +@Api(tags = "可充电储能子系统温度信息") public class SubsystemTemperatureController extends BaseController { @Autowired @@ -37,8 +40,9 @@ public class SubsystemTemperatureController extends BaseController /** * 查询可充电储能子系统温度信息列表 */ - @PreAuthorize("@ss.hasPermi('temperature:temperature:list')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:list')") @GetMapping("/list") + @ApiOperation("查询子系统温度信息列表") public TableDataInfo list(SubsystemTemperature subsystemTemperature) { startPage(); @@ -49,9 +53,10 @@ public class SubsystemTemperatureController extends BaseController /** * 导出可充电储能子系统温度信息列表 */ - @PreAuthorize("@ss.hasPermi('temperature:temperature:export')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:export')") @Log(title = "可充电储能子系统温度信息", businessType = BusinessType.EXPORT) @PostMapping("/export") + @ApiOperation("导出子系统温度信息列表") public void export(HttpServletResponse response, SubsystemTemperature subsystemTemperature) { List list = subsystemTemperatureService.selectSubsystemTemperatureList(subsystemTemperature); @@ -62,8 +67,9 @@ public class SubsystemTemperatureController extends BaseController /** * 获取可充电储能子系统温度信息详细信息 */ - @PreAuthorize("@ss.hasPermi('temperature:temperature:query')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:query')") @GetMapping(value = "/{id}") + @ApiOperation("获取子系统温度信息详细信息") public AjaxResult getInfo(@PathVariable("id") Long id) { return success(subsystemTemperatureService.selectSubsystemTemperatureById(id)); @@ -72,9 +78,10 @@ public class SubsystemTemperatureController extends BaseController /** * 新增可充电储能子系统温度信息 */ - @PreAuthorize("@ss.hasPermi('temperature:temperature:add')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:add')") @Log(title = "可充电储能子系统温度信息", businessType = BusinessType.INSERT) @PostMapping + @ApiOperation("新增子系统温度信息") public AjaxResult add(@RequestBody SubsystemTemperature subsystemTemperature) { return toAjax(subsystemTemperatureService.insertSubsystemTemperature(subsystemTemperature)); @@ -83,9 +90,10 @@ public class SubsystemTemperatureController extends BaseController /** * 修改可充电储能子系统温度信息 */ - @PreAuthorize("@ss.hasPermi('temperature:temperature:edit')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:edit')") @Log(title = "可充电储能子系统温度信息", businessType = BusinessType.UPDATE) @PutMapping + @ApiOperation("修改子系统温度信息") public AjaxResult edit(@RequestBody SubsystemTemperature subsystemTemperature) { return toAjax(subsystemTemperatureService.updateSubsystemTemperature(subsystemTemperature)); @@ -94,9 +102,10 @@ public class SubsystemTemperatureController extends BaseController /** * 删除可充电储能子系统温度信息 */ - @PreAuthorize("@ss.hasPermi('temperature:temperature:remove')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemTemperature:remove')") @Log(title = "可充电储能子系统温度信息", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") + @DeleteMapping("/{ids}") + @ApiOperation("删除子系统温度信息") public AjaxResult remove(@PathVariable Long[] ids) { return toAjax(subsystemTemperatureService.deleteSubsystemTemperatureByIds(ids)); diff --git a/src/main/java/com/evobms/project/battery/controller/SubsystemVoltageController.java b/src/main/java/com/evobms/project/battery/controller/SubsystemVoltageController.java index 16c4b38..2b44afa 100644 --- a/src/main/java/com/evobms/project/battery/controller/SubsystemVoltageController.java +++ b/src/main/java/com/evobms/project/battery/controller/SubsystemVoltageController.java @@ -20,6 +20,8 @@ import com.evobms.framework.web.controller.BaseController; import com.evobms.framework.web.domain.AjaxResult; import com.evobms.common.utils.poi.ExcelUtil; import com.evobms.framework.web.page.TableDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; /** * 可充电储能子系统电压信息Controller @@ -28,7 +30,8 @@ import com.evobms.framework.web.page.TableDataInfo; * @date 2025-11-15 */ @RestController -@RequestMapping("/voltage/voltage") +@RequestMapping("/Battery/SubsystemVoltage") +@Api(tags = "可充电储能子系统电压信息") public class SubsystemVoltageController extends BaseController { @Autowired @@ -37,8 +40,9 @@ public class SubsystemVoltageController extends BaseController /** * 查询可充电储能子系统电压信息列表 */ - @PreAuthorize("@ss.hasPermi('voltage:voltage:list')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:list')") @GetMapping("/list") + @ApiOperation("查询子系统电压信息列表") public TableDataInfo list(SubsystemVoltage subsystemVoltage) { startPage(); @@ -49,9 +53,10 @@ public class SubsystemVoltageController extends BaseController /** * 导出可充电储能子系统电压信息列表 */ - @PreAuthorize("@ss.hasPermi('voltage:voltage:export')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:export')") @Log(title = "可充电储能子系统电压信息", businessType = BusinessType.EXPORT) @PostMapping("/export") + @ApiOperation("导出子系统电压信息列表") public void export(HttpServletResponse response, SubsystemVoltage subsystemVoltage) { List list = subsystemVoltageService.selectSubsystemVoltageList(subsystemVoltage); @@ -62,8 +67,9 @@ public class SubsystemVoltageController extends BaseController /** * 获取可充电储能子系统电压信息详细信息 */ - @PreAuthorize("@ss.hasPermi('voltage:voltage:query')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:query')") @GetMapping(value = "/{id}") + @ApiOperation("获取子系统电压信息详细信息") public AjaxResult getInfo(@PathVariable("id") Long id) { return success(subsystemVoltageService.selectSubsystemVoltageById(id)); @@ -72,9 +78,10 @@ public class SubsystemVoltageController extends BaseController /** * 新增可充电储能子系统电压信息 */ - @PreAuthorize("@ss.hasPermi('voltage:voltage:add')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:add')") @Log(title = "可充电储能子系统电压信息", businessType = BusinessType.INSERT) @PostMapping + @ApiOperation("新增子系统电压信息") public AjaxResult add(@RequestBody SubsystemVoltage subsystemVoltage) { return toAjax(subsystemVoltageService.insertSubsystemVoltage(subsystemVoltage)); @@ -83,9 +90,10 @@ public class SubsystemVoltageController extends BaseController /** * 修改可充电储能子系统电压信息 */ - @PreAuthorize("@ss.hasPermi('voltage:voltage:edit')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:edit')") @Log(title = "可充电储能子系统电压信息", businessType = BusinessType.UPDATE) @PutMapping + @ApiOperation("修改子系统电压信息") public AjaxResult edit(@RequestBody SubsystemVoltage subsystemVoltage) { return toAjax(subsystemVoltageService.updateSubsystemVoltage(subsystemVoltage)); @@ -94,9 +102,10 @@ public class SubsystemVoltageController extends BaseController /** * 删除可充电储能子系统电压信息 */ - @PreAuthorize("@ss.hasPermi('voltage:voltage:remove')") + @PreAuthorize("@ss.hasPermi('Battery:SubsystemVoltage:remove')") @Log(title = "可充电储能子系统电压信息", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") + @DeleteMapping("/{ids}") + @ApiOperation("删除子系统电压信息") public AjaxResult remove(@PathVariable Long[] ids) { return toAjax(subsystemVoltageService.deleteSubsystemVoltageByIds(ids)); diff --git a/src/main/java/com/evobms/project/battery/domain/ExtremeValues.java b/src/main/java/com/evobms/project/battery/domain/ExtremeValues.java index efc1949..73b65e7 100644 --- a/src/main/java/com/evobms/project/battery/domain/ExtremeValues.java +++ b/src/main/java/com/evobms/project/battery/domain/ExtremeValues.java @@ -2,7 +2,11 @@ package com.evobms.project.battery.domain; import java.math.BigDecimal; import java.util.Date; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.evobms.framework.aspectj.lang.annotation.Excel; @@ -14,67 +18,85 @@ import com.evobms.framework.web.domain.BaseEntity; * @author 田志阳 * @date 2025-10-14 */ +@ApiModel(description = "电池极值数据对象") +@Data +@EqualsAndHashCode(callSuper = false) public class ExtremeValues extends BaseEntity { private static final long serialVersionUID = 1L; /** 主键ID */ + @ApiModelProperty(value = "主键ID") private Long id; /** 设备ID */ + @ApiModelProperty(value = "设备ID") @Excel(name = "设备ID") private String deviceId; /** 数据时间戳 */ + @ApiModelProperty(value = "数据时间戳") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date timestamp; /** 最高电压电池子系统号 */ + @ApiModelProperty(value = "最高电压电池子系统号") @Excel(name = "最高电压电池子系统号") private Integer maxVoltageSubsystemNo; /** 最高电压电池单体代号 */ + @ApiModelProperty(value = "最高电压电池单体代号") @Excel(name = "最高电压电池单体代号") private Integer maxVoltageBatteryNo; /** 电池单体电压最高值(V) */ + @ApiModelProperty(value = "电池单体电压最高值(V)") @Excel(name = "电池单体电压最高值(V)") private BigDecimal maxVoltageValue; /** 最低电压电池子系统号 */ + @ApiModelProperty(value = "最低电压电池子系统号") @Excel(name = "最低电压电池子系统号") private Integer minVoltageSubsystemNo; /** 最低电压电池单体代号 */ + @ApiModelProperty(value = "最低电压电池单体代号") @Excel(name = "最低电压电池单体代号") private Integer minVoltageBatteryNo; /** 电池单体电压最低值(V) */ + @ApiModelProperty(value = "电池单体电压最低值(V)") @Excel(name = "电池单体电压最低值(V)") private BigDecimal minVoltageValue; /** 最高温度子系统号 */ + @ApiModelProperty(value = "最高温度子系统号") @Excel(name = "最高温度子系统号") private Integer maxTempSubsystemNo; /** 最高温度探针序号 */ + @ApiModelProperty(value = "最高温度探针序号") @Excel(name = "最高温度探针序号") private Integer maxTempProbeNo; /** 最高温度值(℃) */ + @ApiModelProperty(value = "最高温度值(℃)") @Excel(name = "最高温度值(℃)") private BigDecimal maxTempValue; /** 最低温度子系统号 */ + @ApiModelProperty(value = "最低温度子系统号") @Excel(name = "最低温度子系统号") private Integer minTempSubsystemNo; /** 最低温度探针序号 */ + @ApiModelProperty(value = "最低温度探针序号") @Excel(name = "最低温度探针序号") private Integer minTempProbeNo; /** 最低温度值(℃) */ + @ApiModelProperty(value = "最低温度值(℃)") @Excel(name = "最低温度值(℃)") private BigDecimal minTempValue; diff --git a/src/main/java/com/evobms/project/battery/domain/SubsystemTemperature.java b/src/main/java/com/evobms/project/battery/domain/SubsystemTemperature.java index 28f2fb9..5d69965 100644 --- a/src/main/java/com/evobms/project/battery/domain/SubsystemTemperature.java +++ b/src/main/java/com/evobms/project/battery/domain/SubsystemTemperature.java @@ -1,10 +1,13 @@ package com.evobms.project.battery.domain; import java.util.Date; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import com.evobms.framework.aspectj.lang.annotation.Excel; import com.evobms.framework.web.domain.BaseEntity; +import lombok.EqualsAndHashCode; /** * 可充电储能子系统温度信息对象 subsystem_temperature @@ -13,44 +16,55 @@ import com.evobms.framework.web.domain.BaseEntity; * @date 2025-11-15 */ @Data +@ApiModel(description = "可充电储能子系统温度信息对象") +@EqualsAndHashCode(callSuper = false) public class SubsystemTemperature extends BaseEntity { private static final long serialVersionUID = 1L; /** 主键ID */ + @ApiModelProperty(value = "主键ID") private Long id; /** 设备ID */ + @ApiModelProperty(value = "设备ID") @Excel(name = "设备ID") private String deviceId; /** 数据时间戳 */ + @ApiModelProperty(value = "数据时间戳") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date timestamp; /** 可充电储能子系统个数 */ + @ApiModelProperty(value = "可充电储能子系统个数") @Excel(name = "可充电储能子系统个数") private Integer subsystemCount; /** 可充电储能子系统号 */ + @ApiModelProperty(value = "可充电储能子系统号") @Excel(name = "可充电储能子系统号") private Integer subsystemNo; /** 可充电储能温度探针个数 */ + @ApiModelProperty(value = "可充电储能温度探针个数") @Excel(name = "可充电储能温度探针个数") private Integer tempProbeCount; /** 各温度探针检测的温度值数组(℃) */ + @ApiModelProperty(value = "各温度探针检测的温度值数组(℃)") @Excel(name = "各温度探针检测的温度值数组(℃)") private String temperatureValues; /** 创建时间 */ + @ApiModelProperty(value = "创建时间") @JsonFormat(pattern = "yyyy-MM-dd") @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd") private Date createTime; /** 更新时间 */ + @ApiModelProperty(value = "更新时间") @JsonFormat(pattern = "yyyy-MM-dd") @Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd") private Date updateTime; diff --git a/src/main/java/com/evobms/project/battery/domain/SubsystemVoltage.java b/src/main/java/com/evobms/project/battery/domain/SubsystemVoltage.java index 6dab1c8..7f1e3b3 100644 --- a/src/main/java/com/evobms/project/battery/domain/SubsystemVoltage.java +++ b/src/main/java/com/evobms/project/battery/domain/SubsystemVoltage.java @@ -2,6 +2,8 @@ package com.evobms.project.battery.domain; import java.math.BigDecimal; import java.util.Date; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -16,60 +18,74 @@ import com.evobms.framework.web.domain.BaseEntity; * @date 2025-11-15 */ @Data +@ApiModel(description = "可充电储能子系统电压信息对象") public class SubsystemVoltage extends BaseEntity { private static final long serialVersionUID = 1L; /** 主键ID */ + @ApiModelProperty(value = "主键ID") private Long id; /** 设备ID */ + @ApiModelProperty(value = "设备ID") @Excel(name = "设备ID") private String deviceId; /** 数据时间戳 */ + @ApiModelProperty(value = "数据时间戳") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date timestamp; /** 可充电储能子系统个数 */ + @ApiModelProperty(value = "可充电储能子系统个数") @Excel(name = "可充电储能子系统个数") private Integer subsystemCount; /** 可充电储能子系统号 */ + @ApiModelProperty(value = "可充电储能子系统号") @Excel(name = "可充电储能子系统号") private Integer subsystemNo; /** 可充电储能装置电压(V) */ + @ApiModelProperty(value = "可充电储能装置电压(V)") @Excel(name = "可充电储能装置电压(V)") private BigDecimal subsystemVoltage; /** 可充电储能装置电流(A) */ + @ApiModelProperty(value = "可充电储能装置电流(A)") @Excel(name = "可充电储能装置电流(A)") private BigDecimal subsystemCurrent; /** 单体电池总数 */ + @ApiModelProperty(value = "单体电池总数") @Excel(name = "单体电池总数") private Integer totalBatteryCount; /** 本帧起始电池序号 */ + @ApiModelProperty(value = "本帧起始电池序号") @Excel(name = "本帧起始电池序号") private Integer frameStartBatteryNo; /** 本帧单体电池总数 */ + @ApiModelProperty(value = "本帧单体电池总数") @Excel(name = "本帧单体电池总数") private Integer frameBatteryCount; /** 单体电池电压数组(V) */ + @ApiModelProperty(value = "单体电池电压数组(V)") @Excel(name = "单体电池电压数组(V)") private String batteryVoltages; /** 创建时间 */ + @ApiModelProperty(value = "创建时间") @JsonFormat(pattern = "yyyy-MM-dd") @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd") private Date createTime; /** 更新时间 */ + @ApiModelProperty(value = "更新时间") @JsonFormat(pattern = "yyyy-MM-dd") @Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd") private Date updateTime; diff --git a/src/main/java/com/evobms/project/bms/controller/GBT32960FullDecoder.java b/src/main/java/com/evobms/project/bms/controller/GBT32960FullDecoder.java index 7786a48..4901a24 100644 --- a/src/main/java/com/evobms/project/bms/controller/GBT32960FullDecoder.java +++ b/src/main/java/com/evobms/project/bms/controller/GBT32960FullDecoder.java @@ -7,8 +7,10 @@ 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) { String s = sn == null ? "" : sn; @@ -68,150 +70,236 @@ public class GBT32960FullDecoder { } // 详细的协议解析方法 - public static String parseGBT32960Data(byte[] receivedData, String deviceSn) { - String bytesData = null; - try { - log.info("=== GBT32960协议数据解析 ==="); - log.info("实际数据长度: " + receivedData.length + " 字节"); - // 1. 检查起始位 - if (receivedData.length < 2 || receivedData[0] != 0x23 || receivedData[1] != 0x23) { - log.error("无效的起始位或数据过短"); - return null; - } - log.info("起始位: 0x2323 ✓"); + public static String parseGBT32960Data(byte[] receivedData, String deviceSn) { + String bytesData = null; + try { + log.info("=== GBT32960协议数据解析 ==="); + log.info("实际数据长度: " + receivedData.length + " 字节"); + // 1. 检查起始位 + if (receivedData.length < 2 || receivedData[0] != 0x23 || receivedData[1] != 0x23) { + log.error("无效的起始位或数据过短"); + return null; + } + log.info("起始位: 0x2323 ✓"); - // 2. 打印完整数据用于调试 - log.info("完整数据: " + bytesToHex(receivedData)); + // 2. 打印完整数据用于调试 + log.info("完整数据: " + bytesToHex(receivedData)); - // 3. 分析协议结构 - int offset = 2; // 跳过起始位 + // 3. 分析协议结构 + int offset = 2; // 跳过起始位 - // 命令标识 - byte commandId = receivedData[offset++]; - log.info("命令标识: 0x" + String.format("%02X", commandId)); + // 命令标识 + byte commandId = receivedData[offset++]; + log.info("命令标识: 0x" + String.format("%02X", commandId)); - // 应答标志 - byte responseFlag = receivedData[offset++]; - log.info("应答标志: 0x" + String.format("%02X", responseFlag)); + // 应答标志 + byte responseFlag = receivedData[offset++]; + log.info("应答标志: 0x" + String.format("%02X", responseFlag)); - // VIN码 (17字节) - byte[] vin = Arrays.copyOfRange(receivedData, offset, offset + 17); - offset += 17; - log.info("VIN码: " + bytesToHex(vin) + " (" + new String(vin).trim() + ")"); + // VIN码 (17字节) + byte[] vin = Arrays.copyOfRange(receivedData, offset, offset + 17); + offset += 17; + log.info("VIN码: " + bytesToHex(vin) + " (" + new String(vin).trim() + ")"); - // 加密方式 - byte encryptionMethod = receivedData[offset++]; - log.info("加密方式: 0x" + String.format("%02X", encryptionMethod)); + // 加密方式 + byte encryptionMethod = receivedData[offset++]; + log.info("加密方式: 0x" + String.format("%02X", encryptionMethod)); - if (encryptionMethod != 0x03) { - log.error("不支持的解密方式,期望AES128(0x03)"); - return null; - } + if (encryptionMethod != 0x03) { + log.error("不支持的解密方式,期望AES128(0x03)"); + return null; + } - // 数据单元长度 (WORD, 大端序) - int dataUnitLength = ((receivedData[offset] & 0xFF) << 8) | (receivedData[offset + 1] & 0xFF); - offset += 2; - log.info("声明的数据单元长度: " + dataUnitLength + " 字节"); + // 数据单元长度 (WORD, 大端序) + int dataUnitLength = ((receivedData[offset] & 0xFF) << 8) | (receivedData[offset + 1] & 0xFF); + offset += 2; + log.info("声明的数据单元长度: " + dataUnitLength + " 字节"); - // 计算当前头部长度 - int headerLength = offset; - log.info("协议头部长度: " + headerLength + " 字节"); + // 计算当前头部长度 + int headerLength = offset; + log.info("协议头部长度: " + headerLength + " 字节"); - // 剩余数据长度 - int remainingDataLength = receivedData.length - headerLength; - log.info("剩余数据长度: " + remainingDataLength + " 字节"); + // 剩余数据长度 + int remainingDataLength = receivedData.length - headerLength; + log.info("剩余数据长度: " + remainingDataLength + " 字节"); - // 4. 重新计算期望长度 - // 方案1: 如果BCC是1字节 - int expectedLength1 = headerLength + dataUnitLength + 1; - // 方案2: 如果BCC是2字节(根据您最初描述) - int expectedLength2 = headerLength + dataUnitLength + 2; + // 4. 重新计算期望长度 + // 方案1: 如果BCC是1字节 + int expectedLength1 = headerLength + dataUnitLength + 1; + // 方案2: 如果BCC是2字节(根据您最初描述) + int expectedLength2 = headerLength + dataUnitLength + 2; - log.info("期望长度 (BCC=1字节): " + expectedLength1); - log.info("期望长度 (BCC=2字节): " + expectedLength2); - log.info("实际长度: " + receivedData.length); + log.info("期望长度 (BCC=1字节): " + expectedLength1); + log.info("期望长度 (BCC=2字节): " + expectedLength2); + log.info("实际长度: " + receivedData.length); - // 5. 确定正确的数据范围 - int encryptedDataStart = offset; - int encryptedDataEnd; - int bccStart; + // 5. 确定正确的数据范围 + int encryptedDataStart = offset; + int encryptedDataEnd; + int bccStart; - if (receivedData.length == expectedLength1) { - // BCC为1字节 - encryptedDataEnd = encryptedDataStart + dataUnitLength; - bccStart = encryptedDataEnd; - log.info("采用方案: BCC为1字节"); - } else if (receivedData.length == expectedLength2) { - // BCC为2字节 - encryptedDataEnd = encryptedDataStart + dataUnitLength; - bccStart = encryptedDataEnd; - log.info("采用方案: BCC为2字节"); - } else { - // 自动适应:使用剩余的所有数据作为加密数据 - encryptedDataEnd = receivedData.length - 1; // 假设BCC为1字节 - int actualDataUnitLength = encryptedDataEnd - encryptedDataStart; - log.info("长度不匹配,使用实际数据单元长度: " + actualDataUnitLength + " 字节"); - dataUnitLength = actualDataUnitLength; - bccStart = encryptedDataEnd; - } + if (receivedData.length == expectedLength1) { + // BCC为1字节 + encryptedDataEnd = encryptedDataStart + dataUnitLength; + bccStart = encryptedDataEnd; + log.info("采用方案: BCC为1字节"); + } else if (receivedData.length == expectedLength2) { + // BCC为2字节 + encryptedDataEnd = encryptedDataStart + dataUnitLength; + bccStart = encryptedDataEnd; + log.info("采用方案: BCC为2字节"); + } else { + // 自动适应:使用剩余的所有数据作为加密数据 + encryptedDataEnd = receivedData.length - 1; // 假设BCC为1字节 + int actualDataUnitLength = encryptedDataEnd - encryptedDataStart; + log.info("长度不匹配,使用实际数据单元长度: " + actualDataUnitLength + " 字节"); + dataUnitLength = actualDataUnitLength; + bccStart = encryptedDataEnd; + } - // 6. 提取加密数据 - byte[] encryptedData = Arrays.copyOfRange(receivedData, encryptedDataStart, encryptedDataStart + dataUnitLength); - log.info("加密数据长度: " + encryptedData.length + " 字节"); - log.info("加密数据 (前64字节): " + bytesToHex(Arrays.copyOf(encryptedData, Math.min(64, encryptedData.length))) + "..."); + // 6. 提取加密数据 + byte[] encryptedData = Arrays.copyOfRange(receivedData, encryptedDataStart, encryptedDataStart + dataUnitLength); + log.info("加密数据长度: " + encryptedData.length + " 字节"); + log.info("加密数据 (前64字节): " + bytesToHex(Arrays.copyOf(encryptedData, Math.min(64, encryptedData.length))) + "..."); + if (encryptedData.length % 16 != 0) { + log.error("AES密文长度非法,不是16倍数,length=" + encryptedData.length); + return null; + } + // 7. 提取BCC校验码 + int bccLength = receivedData.length - (encryptedDataStart + dataUnitLength); + byte[] receivedBCC = Arrays.copyOfRange(receivedData, encryptedDataStart + dataUnitLength, receivedData.length); + //log.info("BCC长度: " + bccLength + " 字节"); + //log.info("接收到的BCC: " + bytesToHex(receivedBCC)); - // 7. 提取BCC校验码 - int bccLength = receivedData.length - (encryptedDataStart + dataUnitLength); - byte[] receivedBCC = Arrays.copyOfRange(receivedData, encryptedDataStart + dataUnitLength, receivedData.length); - //log.info("BCC长度: " + bccLength + " 字节"); - //log.info("接收到的BCC: " + bytesToHex(receivedBCC)); + // 8. 计算BCC校验范围:从命令单元开始到加密数据结束 + int bccCalcStart = 2; // 从命令标识开始 (跳过0x2323) + int bccCalcEnd = encryptedDataStart + dataUnitLength; + byte calculatedBCC = calculateBCC(receivedData, bccCalcStart, bccCalcEnd); - // 8. 计算BCC校验范围:从命令单元开始到加密数据结束 - int bccCalcStart = 2; // 从命令标识开始 (跳过0x2323) - int bccCalcEnd = encryptedDataStart + dataUnitLength; - byte calculatedBCC = calculateBCC(receivedData, bccCalcStart, bccCalcEnd); + log.info("BCC计算范围: 字节[" + bccCalcStart + "~" + bccCalcEnd + ")"); + log.info("计算出的BCC: 0x" + String.format("%02X", calculatedBCC)); - log.info("BCC计算范围: 字节[" + bccCalcStart + "~" + bccCalcEnd + ")"); - log.info("计算出的BCC: 0x" + String.format("%02X", calculatedBCC)); - - // 验证BCC (只比较第一个字节) - if (receivedBCC.length > 0 && calculatedBCC == receivedBCC[0]) { - log.info("BCC校验通过 ✓"); - } else { - log.error("BCC校验失败!"); - } + // 验证BCC (只比较第一个字节) + if (receivedBCC.length > 0 && calculatedBCC == receivedBCC[0]) { + log.info("BCC校验通过 ✓"); + } else { + log.error("BCC校验失败!"); + } - //使用的AES密钥 - byte[] aesKey = generateKeyFromSn(deviceSn); - System.out.println("密钥------------------------>"+aesKey); - byte[] decryptedData = decryptData(encryptedData, aesKey); - bytesData = bytesToHex(decryptedData); - log.info("解密成功!"); - log.info("解密后数据长度: " + decryptedData.length + " 字节"); - log.info("解密后数据 (完整): " + bytesData); + //使用的AES密钥 + byte[] aesKey = generateKeyFromSn(deviceSn); + System.out.println(Arrays.toString(aesKey)); + System.out.println(aesKey.length); + log.info("AES密钥: " + bytesToHex(aesKey)); + byte[] decryptedData = decryptData(encryptedData, aesKey); + bytesData = bytesToHex(decryptedData); + log.info("解密成功!"); + log.info("解密后数据长度: " + decryptedData.length + " 字节"); + log.info("解密后数据 (完整): " + bytesData); - // 10. 解析解密后的数据单元 - // parseDecryptedDataUnit(decryptedData); + // 10. 解析解密后的数据单元 + // parseDecryptedDataUnit(decryptedData); - } catch (Exception e) { - log.error("解析过程中发生错误: " + e.getMessage()); - e.printStackTrace(); + } catch (Exception e) { + log.error("解析过程中发生错误: " + e.getMessage()); + e.printStackTrace(); + } + return bytesData; + } + + /* public static String parseGBT32960Data(byte[] frame, String deviceSn) throws Exception { + + if (frame == null || frame.length < 25) { + throw new IllegalArgumentException("报文长度非法"); } - return bytesData; + int offset = 0; + // 1. 起始符校验 0x23 0x23 + + if (frame[offset++] != 0x23 || frame[offset++] != 0x23) { + throw new IllegalArgumentException("非法起始位,非0x2323"); + } + // 2. 命令标识 + byte commandId = frame[offset++]; + // 3. 应答标志 + byte responseFlag = frame[offset++]; + // 4. VIN (17字节) + byte[] vinBytes = Arrays.copyOfRange(frame, offset, offset + 17); + String vin = new String(vinBytes).trim(); + offset += 17; + // 5. 加密方式 + byte encryptType = frame[offset++]; + // 6. 数据单元长度 (大端) + if (offset + 2 > frame.length) { + throw new IllegalArgumentException("数据单元长度字段越界"); + } + int dataUnitLength = ((frame[offset] & 0xFF) << 8) | (frame[offset + 1] & 0xFF); + offset += 2; + int headerLength = offset; + + // 7. 校验整体长度 + // 标准格式: + // 头部 + 数据单元 + 1字节BCC + int expectedLength = headerLength + dataUnitLength + 1; + if (frame.length != expectedLength) { + throw new IllegalArgumentException("报文长度不匹配,声明长度=" + dataUnitLength + ",实际长度=" + frame.length + ",期望总长度=" + expectedLength); + } + // 8. 提取数据单元 + byte[] dataUnit = Arrays.copyOfRange(frame, headerLength, headerLength + dataUnitLength + ); + // 9. BCC 校验 + // BCC范围:从命令标识开始 + byte receivedBcc = frame[frame.length - 1]; + byte calculatedBcc = calculateBCC(frame, 2, headerLength + dataUnitLength + ); + if (receivedBcc != calculatedBcc) { + throw new IllegalStateException( + String.format("BCC校验失败,收到=0x%02X 计算=0x%02X", + receivedBcc, calculatedBcc) + ); + } + + + // 10. 加密处理 + + if (encryptType == 0x01) { + // 不加密 + return bytesToHex(dataUnit); + } + + if (encryptType != 0x03) { + throw new UnsupportedOperationException( + "不支持的加密方式: " + encryptType); + } + + // AES 必须 16倍数 + if (dataUnitLength % 16 != 0) { + throw new IllegalStateException( + "AES密文长度非法,不是16倍数,length=" + dataUnitLength); + } + + + // 11. AES解密 + + byte[] aesKey = generateKeyFromSn(deviceSn); + + byte[] decrypted = decryptData(dataUnit, aesKey); + + return bytesToHex(decrypted); } +*/ private static void parseDecryptedDataUnit(byte[] decryptedData) { - log.info("\n=== 数据单元解析 ==="); + log.info("\n=== 数据单元解析 ==="); if (decryptedData == null || decryptedData.length == 0) { - log.info("解密数据为空"); + log.info("解密数据为空"); return; } - log.info("数据单元总长度: " + decryptedData.length + " 字节"); - log.info("数据单元内容: " + bytesToHex(decryptedData)); + log.info("数据单元总长度: " + decryptedData.length + " 字节"); + log.info("数据单元内容: " + bytesToHex(decryptedData)); // 尝试解析采集时间(前6字节BCD) if (decryptedData.length >= 6) { @@ -280,16 +368,26 @@ public class GBT32960FullDecoder { private static String typeName(int typeId) { switch (typeId & 0xFF) { - case 0x01: return "整车数据"; - case 0x02: return "驱动电机数据"; - case 0x03: return "燃料电池数据"; - case 0x04: return "发动机数据"; - case 0x05: return "车辆位置数据"; - case 0x06: return "极值数据"; - case 0x07: return "报警数据"; - case 0x08: return "储能装置电压数据"; - case 0x09: return "储能装置温度数据"; - default: return String.format("未知类型(0x%02X)", typeId); + case 0x01: + return "整车数据"; + case 0x02: + return "驱动电机数据"; + case 0x03: + return "燃料电池数据"; + case 0x04: + return "发动机数据"; + case 0x05: + return "车辆位置数据"; + case 0x06: + return "极值数据"; + case 0x07: + return "报警数据"; + case 0x08: + return "储能装置电压数据"; + case 0x09: + return "储能装置温度数据"; + default: + return String.format("未知类型(0x%02X)", typeId); } } diff --git a/src/main/java/com/evobms/project/bms/controller/MqttPublishController.java b/src/main/java/com/evobms/project/bms/controller/MqttPublishController.java index 7584d49..03f0edb 100644 --- a/src/main/java/com/evobms/project/bms/controller/MqttPublishController.java +++ b/src/main/java/com/evobms/project/bms/controller/MqttPublishController.java @@ -5,6 +5,8 @@ import com.evobms.common.constant.Constants; import com.evobms.common.constant.BboxApiConstants; import com.evobms.common.utils.StringUtils; import com.evobms.project.bms.service.MqttService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import javax.servlet.http.HttpServletRequest; @@ -25,6 +27,7 @@ import org.slf4j.LoggerFactory; */ @RestController @RequestMapping("/bms/mqtt") +@Api(tags = "MQTT消息发布") public class MqttPublishController { private static final Logger log = LoggerFactory.getLogger(MqttPublishController.class); @@ -39,6 +42,7 @@ public class MqttPublishController { */ @PostMapping("/publish") // @Log(title = "MQTT消息发布", businessType = BusinessType.INSERT) + @ApiOperation("发布文本消息") public AjaxResult publishMessage(@RequestParam String topic, @RequestParam String message) { try { if (mqttService == null) { @@ -58,6 +62,7 @@ public class MqttPublishController { * 发布原始十六进制字节消息到指定主题 */ @PostMapping("/publishHex") + @ApiOperation("发布HEX消息") public AjaxResult publishHex(@RequestParam String topic, @RequestParam String hex) { try { if (mqttService == null) { @@ -78,6 +83,7 @@ public class MqttPublishController { * 获取当前请求的鉴权令牌(从请求头读取并去除前缀) */ @GetMapping("/authToken") + @ApiOperation("获取鉴权令牌") public AjaxResult getAuthToken(HttpServletRequest request) { try { String raw = request.getHeader(tokenHeader); @@ -129,6 +135,7 @@ public class MqttPublishController { */ @PostMapping("/publishDeviceData") // @Log(title = "设备数据发布", businessType = BusinessType.INSERT) + @ApiOperation("发布设备测试数据") public AjaxResult publishDeviceData(@RequestParam String deviceCode) { try { if (mqttService == null) { @@ -160,6 +167,7 @@ public class MqttPublishController { * 快速测试 - 发布消息到test设备 */ @GetMapping("/quickTest") + @ApiOperation("快速测试发布") public AjaxResult quickTest() { try { if (mqttService == null) { @@ -192,6 +200,7 @@ public class MqttPublishController { */ @PostMapping("/batchPublish") // @Log(title = "批量MQTT消息发布", businessType = BusinessType.INSERT) + @ApiOperation("批量发布测试消息") public AjaxResult batchPublish(@RequestParam String deviceCode, @RequestParam(defaultValue = "5") int count) { try { if (mqttService == null) { diff --git a/src/main/java/com/evobms/project/bms/controller/MqttTestController.java b/src/main/java/com/evobms/project/bms/controller/MqttTestController.java index 5aa32e3..ce5da7f 100644 --- a/src/main/java/com/evobms/project/bms/controller/MqttTestController.java +++ b/src/main/java/com/evobms/project/bms/controller/MqttTestController.java @@ -1,6 +1,8 @@ package com.evobms.project.bms.controller; import com.evobms.project.bms.service.MqttService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.slf4j.Logger; @@ -17,6 +19,7 @@ import java.util.Map; */ @RestController @RequestMapping("/mqtt/test") +@Api(tags = "MQTT测试") public class MqttTestController { private static final Logger log = LoggerFactory.getLogger(MqttTestController.class); @@ -31,6 +34,7 @@ public class MqttTestController { * @return 测试结果 */ @PostMapping("/handleDeviceData") + @ApiOperation("测试处理设备数据") public Map testHandleDeviceData( @RequestParam String topic, @RequestParam byte[] payload) { @@ -70,6 +74,7 @@ public class MqttTestController { * @return 测试用的JSON数据 */ @GetMapping("/generateTestData") + @ApiOperation("生成测试数据") public Map generateTestData() { Map testData = new HashMap<>(); testData.put("voltage", 12.5); diff --git a/src/main/java/com/evobms/project/bms/domain/BmsChargeDetail.java b/src/main/java/com/evobms/project/bms/domain/BmsChargeDetail.java new file mode 100644 index 0000000..6a7cf38 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/domain/BmsChargeDetail.java @@ -0,0 +1,75 @@ +package com.evobms.project.bms.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import com.evobms.framework.aspectj.lang.annotation.Excel; +import com.evobms.framework.web.domain.BaseEntity; + +@Data +@ApiModel(description = "BMS充电过程详细数据") +public class BmsChargeDetail extends BaseEntity { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键ID") + private Long id; + + @ApiModelProperty("设备ID") + @Excel(name = "设备ID") + private String deviceId; + + @ApiModelProperty("数据时间戳") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date timestamp; + + @ApiModelProperty("慢充电流(A)") + @Excel(name = "慢充电流(A)") + private java.math.BigDecimal acChargeCurrent; + + @ApiModelProperty("慢充电压(V)") + @Excel(name = "慢充电压(V)") + private java.math.BigDecimal acChargeVoltage; + + @ApiModelProperty("快充电流(A)") + @Excel(name = "快充电流(A)") + private java.math.BigDecimal dcChargeCurrent; + + @ApiModelProperty("快充电压(V)") + @Excel(name = "快充电压(V)") + private java.math.BigDecimal dcChargeVoltage; + + @ApiModelProperty("充电时长(min)") + @Excel(name = "充电时长(min)") + private Integer chargeTime; + + @ApiModelProperty("慢充停止码") + @Excel(name = "慢充停止码") + private Integer acStopCode; + + @ApiModelProperty("快充停止码") + @Excel(name = "快充停止码") + private Integer dcStopCode; + + @ApiModelProperty("慢充CP值") + @Excel(name = "慢充CP值") + private Integer acCpValue; + + @ApiModelProperty("慢充枪阻值") + @Excel(name = "慢充枪阻值") + private Integer acGunResistance; + + @ApiModelProperty("充电阶段") + @Excel(name = "充电阶段") + private Integer chargingStage; + + @ApiModelProperty("慢充阶段") + @Excel(name = "慢充阶段") + private Integer acStage; + + @ApiModelProperty("快充阶段") + @Excel(name = "快充阶段") + private Integer dcStage; +} diff --git a/src/main/java/com/evobms/project/bms/domain/BmsSopData.java b/src/main/java/com/evobms/project/bms/domain/BmsSopData.java new file mode 100644 index 0000000..41a8de4 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/domain/BmsSopData.java @@ -0,0 +1,67 @@ +package com.evobms.project.bms.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import com.evobms.framework.aspectj.lang.annotation.Excel; +import com.evobms.framework.web.domain.BaseEntity; + +@Data +@ApiModel(description = "BMS功率能力(SOP)数据") +public class BmsSopData extends BaseEntity { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键ID") + private Long id; + + @ApiModelProperty("设备ID") + @Excel(name = "设备ID") + private String deviceId; + + @ApiModelProperty("数据时间戳") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date timestamp; + + @ApiModelProperty("10秒放电SOP(A)") + @Excel(name = "10秒放电SOP(A)") + private Integer dischargeSop10s; + + @ApiModelProperty("30秒放电SOP(A)") + @Excel(name = "30秒放电SOP(A)") + private Integer dischargeSop30s; + + @ApiModelProperty("60秒放电SOP(A)") + @Excel(name = "60秒放电SOP(A)") + private Integer dischargeSop60s; + + @ApiModelProperty("持续放电SOP(A)") + @Excel(name = "持续放电SOP(A)") + private Integer dischargeSopContinuous; + + @ApiModelProperty("10秒回馈SOP(A)") + @Excel(name = "10秒回馈SOP(A)") + private Integer regenSop10s; + + @ApiModelProperty("30秒回馈SOP(A)") + @Excel(name = "30秒回馈SOP(A)") + private Integer regenSop30s; + + @ApiModelProperty("60秒回馈SOP(A)") + @Excel(name = "60秒回馈SOP(A)") + private Integer regenSop60s; + + @ApiModelProperty("持续回馈SOP(A)") + @Excel(name = "持续回馈SOP(A)") + private Integer regenSopContinuous; + + @ApiModelProperty("充电SOP(A)") + @Excel(name = "充电SOP(A)") + private Integer chargeSop; + + @ApiModelProperty("慢充SOP(A)") + @Excel(name = "慢充SOP(A)") + private Integer acChargeSop; +} diff --git a/src/main/java/com/evobms/project/bms/domain/BmsSystemStatus.java b/src/main/java/com/evobms/project/bms/domain/BmsSystemStatus.java new file mode 100644 index 0000000..0d65d05 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/domain/BmsSystemStatus.java @@ -0,0 +1,59 @@ +package com.evobms.project.bms.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import com.evobms.framework.aspectj.lang.annotation.Excel; +import com.evobms.framework.web.domain.BaseEntity; + +@Data +@ApiModel(description = "BMS系统状态数据") +public class BmsSystemStatus extends BaseEntity { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键ID") + private Long id; + + @ApiModelProperty("设备ID") + @Excel(name = "设备ID") + private String deviceId; + + @ApiModelProperty("数据时间戳") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date timestamp; + + @ApiModelProperty("继电器状态") + @Excel(name = "继电器状态") + private Integer relayStatus; + + @ApiModelProperty("ON/A+/CC状态") + @Excel(name = "ON/A+/CC状态") + private Integer onaaccStatus; + + @ApiModelProperty("系统复位次数") + @Excel(name = "系统复位次数") + private Integer resetCount; + + @ApiModelProperty("电池循环次数") + @Excel(name = "电池循环次数") + private Integer cycleCount; + + @ApiModelProperty("2.5V采样电压(V)") + @Excel(name = "2.5V采样电压(V)") + private java.math.BigDecimal sample25v; + + @ApiModelProperty("5V采样电压(V)") + @Excel(name = "5V采样电压(V)") + private java.math.BigDecimal sample5v; + + @ApiModelProperty("单体数量") + @Excel(name = "单体数量") + private Integer cellCount; + + @ApiModelProperty("温度探头数量") + @Excel(name = "温度探头数量") + private Integer tempSensorCount; +} diff --git a/src/main/java/com/evobms/project/bms/mapper/BmsChargeDetailMapper.java b/src/main/java/com/evobms/project/bms/mapper/BmsChargeDetailMapper.java new file mode 100644 index 0000000..b605020 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/mapper/BmsChargeDetailMapper.java @@ -0,0 +1,9 @@ +package com.evobms.project.bms.mapper; + +import com.evobms.project.bms.domain.BmsChargeDetail; +import java.util.List; + +public interface BmsChargeDetailMapper { + int insertBmsChargeDetail(BmsChargeDetail data); + List selectBmsChargeDetailList(BmsChargeDetail filter); +} diff --git a/src/main/java/com/evobms/project/bms/mapper/BmsSopDataMapper.java b/src/main/java/com/evobms/project/bms/mapper/BmsSopDataMapper.java new file mode 100644 index 0000000..65e6660 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/mapper/BmsSopDataMapper.java @@ -0,0 +1,9 @@ +package com.evobms.project.bms.mapper; + +import com.evobms.project.bms.domain.BmsSopData; +import java.util.List; + +public interface BmsSopDataMapper { + int insertBmsSopData(BmsSopData data); + List selectBmsSopDataList(BmsSopData filter); +} diff --git a/src/main/java/com/evobms/project/bms/mapper/BmsSystemStatusMapper.java b/src/main/java/com/evobms/project/bms/mapper/BmsSystemStatusMapper.java new file mode 100644 index 0000000..1ae6a08 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/mapper/BmsSystemStatusMapper.java @@ -0,0 +1,9 @@ +package com.evobms.project.bms.mapper; + +import com.evobms.project.bms.domain.BmsSystemStatus; +import java.util.List; + +public interface BmsSystemStatusMapper { + int insertBmsSystemStatus(BmsSystemStatus data); + List selectBmsSystemStatusList(BmsSystemStatus filter); +} diff --git a/src/main/java/com/evobms/project/bms/service/IBmsChargeDetailService.java b/src/main/java/com/evobms/project/bms/service/IBmsChargeDetailService.java new file mode 100644 index 0000000..b50ff56 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/service/IBmsChargeDetailService.java @@ -0,0 +1,7 @@ +package com.evobms.project.bms.service; + +import com.evobms.project.bms.domain.BmsChargeDetail; + +public interface IBmsChargeDetailService { + int insertBmsChargeDetail(BmsChargeDetail data); +} diff --git a/src/main/java/com/evobms/project/bms/service/IBmsSopDataService.java b/src/main/java/com/evobms/project/bms/service/IBmsSopDataService.java new file mode 100644 index 0000000..8436310 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/service/IBmsSopDataService.java @@ -0,0 +1,7 @@ +package com.evobms.project.bms.service; + +import com.evobms.project.bms.domain.BmsSopData; + +public interface IBmsSopDataService { + int insertBmsSopData(BmsSopData data); +} diff --git a/src/main/java/com/evobms/project/bms/service/IBmsSystemStatusService.java b/src/main/java/com/evobms/project/bms/service/IBmsSystemStatusService.java new file mode 100644 index 0000000..01afb85 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/service/IBmsSystemStatusService.java @@ -0,0 +1,7 @@ +package com.evobms.project.bms.service; + +import com.evobms.project.bms.domain.BmsSystemStatus; + +public interface IBmsSystemStatusService { + int insertBmsSystemStatus(BmsSystemStatus data); +} diff --git a/src/main/java/com/evobms/project/bms/service/MqttService.java b/src/main/java/com/evobms/project/bms/service/MqttService.java index ec21c82..ec12864 100644 --- a/src/main/java/com/evobms/project/bms/service/MqttService.java +++ b/src/main/java/com/evobms/project/bms/service/MqttService.java @@ -261,30 +261,32 @@ public class MqttService { */ private void parseByProtocolIndex(byte[] message, String deviceSn) { try { - // 1. 时间信息 - 索引24-29 (6字节) + // 1.时间戳- 索引24-29 (6字节) Date frameTime = parseTimeAtIndex(message, 0, deviceSn); if (frameTime == null) { frameTime = new Date(); } - // 2. 整车数据 - 索引30-49 (20字节) + // 2. 整车数据 - 绝对索引30-49(20字节) | 相对偏移: 6-25 parseVehicleDataAtIndex(message, 6, deviceSn, frameTime); - // 3. 位置信息 - 索引51-60 (10字节) + // 3. 位置信息 - 绝对索引51-60(10字节) | 相对偏移: 27-36 parseLocationDataAtIndex(message, 27, deviceSn, frameTime); - // 4. 极值数据 - 索引61-75 (14字节) + // 4. 极值数据 - 绝对索引61-75(15字节) | 相对偏移: 37-51 parseExtremeDataAtIndex(message, 37, deviceSn, frameTime); - // 5. 单体电压数据 - 索引76-447 (372字节) + // 5. 单体电压数据 - 绝对索引76-447(372字节) | 相对偏移: 52-423 parseCellVoltageAtIndex(message, 52, deviceSn, frameTime); - // 6. 单体温度数据 - 索引448-542 (95字节) + // 6. 单体温度数据 - 绝对索引448-542(95字节) | 相对偏移: 424-518 parseCellTemperatureAtIndex(message, 424, deviceSn, frameTime); - // 7. 累计充放电量 - 索引543-553 (11字节) + // 7. 累计充放电量 - 绝对索引543-553(11字节) | 相对偏移: 519-529 parseChargeDischargeAtIndex(message, 519, deviceSn, frameTime); + parseExtendedDataAtIndex(message, 506, deviceSn, frameTime); + log.info("设备 {} 所有数据段解析完成", deviceSn); } catch (Exception e) { @@ -293,7 +295,7 @@ public class MqttService { } /** - * 解析时间信息 - 索引24-29 (6字节) + * 解析时间信息 - 绝对索引24-29(6字节) | 相对偏移: 0-5 * 格式: 年(偏移2000) + 月 + 日 + 时 + 分 + 秒 */ private Date parseTimeAtIndex(byte[] message, int startIndex, String deviceSn) { @@ -330,7 +332,7 @@ public class MqttService { } /** - * 解析整车数据 - 索引30-49 (20字节) + * 解析整车数据 - 绝对索引30-49(20字节) | 相对偏移: 6-25 */ private void parseVehicleDataAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) { try { @@ -381,7 +383,7 @@ public class MqttService { /** - * 解析位置信息 - 索引51-60 (10字节) + * 解析位置信息 - 绝对索引51-60(10字节) | 相对偏移: 27-36 */ private void parseLocationDataAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) { try { @@ -431,7 +433,7 @@ public class MqttService { } /** - * 解析极值数据 - 索引61-75 (14字节) + * 解析极值数据 - 绝对索引61-75(15字节) | 相对偏移: 37-51 */ private void parseExtremeDataAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) { try { @@ -492,7 +494,7 @@ public class MqttService { /** - * 解析单体电压数据 - 索引76-447 (372字节) + * 解析单体电压数据 - 绝对索引76-447(372字节) | 相对偏移: 52-423 */ private void parseCellVoltageAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) { try { @@ -575,7 +577,7 @@ public class MqttService { /** - * 解析单体温度数据 - 索引448-542 (95字节) + * 解析单体温度数据 - 绝对索引448-542(95字节) | 相对偏移: 424-518 */ private void parseCellTemperatureAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) { try { @@ -638,7 +640,7 @@ public class MqttService { } /** - * 解析累计充放电量 - 索引543-553 (11字节) + * 解析累计充放电量 - 绝对索引543-553(11字节) | 相对偏移: 519-529 */ private void parseChargeDischargeAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) { try { @@ -646,7 +648,6 @@ public class MqttService { log.warn("设备 {}: 充放电数据索引越界", deviceSn); return; } - // 索引543: 数据标识 (应该是0x80) byte dataFlag = message[startIndex]; if (dataFlag != (byte) 0x80) { @@ -658,16 +659,10 @@ public class MqttService { // 跳过索引544-545的保留字节 // 索引546-549: 累计放电电量 (0.1kWh) - long dischargeEnergy = ((message[startIndex + 3] & 0xFFL) << 24) | - ((message[startIndex + 4] & 0xFFL) << 16) | - ((message[startIndex + 5] & 0xFFL) << 8) | - (message[startIndex + 6] & 0xFFL); + long dischargeEnergy = ((message[startIndex + 3] & 0xFFL) << 24) | ((message[startIndex + 4] & 0xFFL) << 16) | ((message[startIndex + 5] & 0xFFL) << 8) | (message[startIndex + 6] & 0xFFL); // 索引550-553: 累计充电电量 (0.1kWh) - long chargeEnergy = ((message[startIndex + 7] & 0xFFL) << 24) | - ((message[startIndex + 8] & 0xFFL) << 16) | - ((message[startIndex + 9] & 0xFFL) << 8) | - (message[startIndex + 10] & 0xFFL); + long chargeEnergy = ((message[startIndex + 7] & 0xFFL) << 24) |((message[startIndex + 8] & 0xFFL) << 16) | ((message[startIndex + 9] & 0xFFL) << 8) | (message[startIndex + 10] & 0xFFL); double dischargeKwh = dischargeEnergy * 0.1; double chargeKwh = chargeEnergy * 0.1; @@ -944,6 +939,132 @@ public class MqttService { sendDeviceCommand(deviceCode, "STATUS_QUERY"); } + /** + * 扩展区块解析(BatteryData[530..716]) + * 索引换算: 绝对索引abs → message索引 = startIndex + (abs - 530) + * 单位与偏移: + * - 电压/电流 ×0.1(除特殊 2.5V/5V 采样为 ×0.001) + * - 温度值需减去 40(协议偏移) + * - 车速 ×0.1 km/h + * - 里程为 u32 原始值(单位由上位业务确定) + */ + private void parseExtendedDataAtIndex(byte[] message, int startIndex, String deviceSn, Date frameTime) { + try { + int base = startIndex; + int need = (716 - 530 + 1); + if (message.length < base + need) { + log.warn("设备 {}: 扩展数据索引越界", deviceSn); + return; + } + // 系统计数/绝缘/车速/总压 + long resetCount = ((message[base + 0] & 0xFFL) << 24) | ((message[base + 1] & 0xFFL) << 16) | ((message[base + 2] & 0xFFL) << 8) | (message[base + 3] & 0xFFL); // 复位次数(u32) + int insulationVal = ((message[base + 4] & 0xFF) << 8) | (message[base + 5] & 0xFF); // 绝缘值(单位KΩ) + int carSpdRaw = ((message[base + 6] & 0xFF) << 8) | (message[base + 7] & 0xFF); // 车速原始(×0.1km/h) + double carSpd = carSpdRaw * 0.1; + int gpvRaw = ((message[base + 8] & 0xFF) << 8) | (message[base + 9] & 0xFF); // GPV(×0.1V) + double gpv = gpvRaw * 0.1; + // RTP1..7(×0.1V) + double rtp1 = (((message[base + 10] & 0xFF) << 8) | (message[base + 11] & 0xFF)) * 0.1; + double rtp2 = (((message[base + 12] & 0xFF) << 8) | (message[base + 13] & 0xFF)) * 0.1; + double rtp3 = (((message[base + 14] & 0xFF) << 8) | (message[base + 15] & 0xFF)) * 0.1; + double rtp4 = (((message[base + 16] & 0xFF) << 8) | (message[base + 17] & 0xFF)) * 0.1; + double rtp5 = (((message[base + 18] & 0xFF) << 8) | (message[base + 19] & 0xFF)) * 0.1; + double rtp6 = (((message[base + 20] & 0xFF) << 8) | (message[base + 21] & 0xFF)) * 0.1; + double rtp7 = (((message[base + 22] & 0xFF) << 8) | (message[base + 23] & 0xFF)) * 0.1; + // RTN探针序号 + int rtn1 = message[base + 24] & 0xFF; // RTN1 + int rtn2 = message[base + 25] & 0xFF; // RTN2 + int rtn3 = message[base + 26] & 0xFF; // RTN3 + int rtn4 = message[base + 27] & 0xFF; // RTN4 + double realSocMax = (((message[base + 28] & 0xFF) << 8) | (message[base + 29] & 0xFF)) * 0.1; // RealSOC最大(×0.1%) + double realSocMin = (((message[base + 30] & 0xFF) << 8) | (message[base + 31] & 0xFF)) * 0.1; // RealSOC最小(×0.1%) + // 充电座温度(℃,需减40) + int chgBs1Tmp = (message[base + 32] & 0xFF) - 40; + int chgBs2Tmp = (message[base + 33] & 0xFF) - 40; + int chgBs3Tmp = (message[base + 34] & 0xFF) - 40; + int chgBs4Tmp = (message[base + 35] & 0xFF) - 40; + int chgBs5Tmp = (message[base + 36] & 0xFF) - 40; + int chgBs6Tmp = (message[base + 37] & 0xFF) - 40; + int chgBs7Tmp = (message[base + 38] & 0xFF) - 40; + int chgBs8Tmp = (message[base + 39] & 0xFF) - 40; + // 慢充/快充电流电压(×0.1) + double tChgCur = (((message[base + 40] & 0xFF) << 8) | (message[base + 41] & 0xFF)) * 0.1; // 慢充电流(A) + double tChgVol = (((message[base + 42] & 0xFF) << 8) | (message[base + 43] & 0xFF)) * 0.1; // 慢充电压(V) + double fChgCur = (((message[base + 44] & 0xFF) << 8) | (message[base + 45] & 0xFF)) * 0.1; // 快充电流(A) + double fChgVol = (((message[base + 46] & 0xFF) << 8) | (message[base + 47] & 0xFF)) * 0.1; // 快充电压(V) + // 放电/回馈SOP能力(A) + int disSop10 = ((message[base + 48] & 0xFF) << 8) | (message[base + 49] & 0xFF); + int disSop30 = ((message[base + 50] & 0xFF) << 8) | (message[base + 51] & 0xFF); + int disSop60 = ((message[base + 52] & 0xFF) << 8) | (message[base + 53] & 0xFF); + int disSopCont = ((message[base + 54] & 0xFF) << 8) | (message[base + 55] & 0xFF); + int regSop10 = ((message[base + 56] & 0xFF) << 8) | (message[base + 57] & 0xFF); + int regSop30 = ((message[base + 58] & 0xFF) << 8) | (message[base + 59] & 0xFF); + int regSop60 = ((message[base + 60] & 0xFF) << 8) | (message[base + 61] & 0xFF); + int regSopCont = ((message[base + 62] & 0xFF) << 8) | (message[base + 63] & 0xFF); + int disSopTol = ((message[base + 64] & 0xFF) << 8) | (message[base + 65] & 0xFF); + int regSopTol = ((message[base + 66] & 0xFF) << 8) | (message[base + 67] & 0xFF); + // 版本与编码 + int hwVer = ((message[base + 68] & 0xFF) << 8) | (message[base + 69] & 0xFF); // 硬件版本 + int swVer = ((message[base + 70] & 0xFF) << 8) | (message[base + 71] & 0xFF); // 软件版本 + String bmsCode = readAsciiRange(message, 602, 28, base); // BMS编码(28字节ASCII) + String vin = readAsciiRange(message, 629, 21, base); // VIN码(21字节ASCII) + // 系统状态 + int sysVolRaw = ((message[base + (649 - 530)] & 0xFF) << 8) | (message[base + (650 - 530)] & 0xFF); // 系统电压(×0.1V) + int sysCurRaw = ((message[base + (651 - 530)] & 0xFF) << 8) | (message[base + (652 - 530)] & 0xFF); // 系统电流(×0.1A) + double sysVol = sysVolRaw * 0.1; + double sysCur = sysCurRaw * 0.1; + int cellCount = ((message[base + (653 - 530)] & 0xFF) << 8) | (message[base + (654 - 530)] & 0xFF); + int tempSensorCount = ((message[base + (655 - 530)] & 0xFF) << 8) | (message[base + (656 - 530)] & 0xFF); + int relayStatus = ((message[base + (657 - 530)] & 0xFF) << 8) | (message[base + (658 - 530)] & 0xFF); + int onaaccStatus = ((message[base + (659 - 530)] & 0xFF) << 8) | (message[base + (660 - 530)] & 0xFF); + int acStopCode = message[base + (661 - 530)] & 0xFF; + int dcStopCode = message[base + (662 - 530)] & 0xFF; + int cycleCount = ((message[base + (663 - 530)] & 0xFF) << 8) | (message[base + (664 - 530)] & 0xFF); + long devId = ((message[base + (665 - 530)] & 0xFFL) << 24) | ((message[base + (666 - 530)] & 0xFFL) << 16) | ((message[base + (667 - 530)] & 0xFFL) << 8) | (message[base + (668 - 530)] & 0xFFL); + String clientCode = readAsciiRange(message, 669, 28, base); // 客户编码(28字节ASCII) + double samp25v = (((message[base + (696 - 530)] & 0xFF) << 8) | (message[base + (697 - 530)] & 0xFF)) * 0.001; // 2.5V采样(×0.001V) + double samp5v = (((message[base + (698 - 530)] & 0xFF) << 8) | (message[base + (699 - 530)] & 0xFF)) * 0.001; // 5V采样(×0.001V) + int chargeSop = ((message[base + (700 - 530)] & 0xFF) << 8) | (message[base + (701 - 530)] & 0xFF); // 充电SOP(A) + int acChargeSop = ((message[base + (702 - 530)] & 0xFF) << 8) | (message[base + (703 - 530)] & 0xFF); // 慢充SOP(A) + // 运行阶段/充电阶段/时间与里程 + int runSts = message[base + (704 - 530)] & 0xFF; // 行车阶段 + int runPwrSts = message[base + (705 - 530)] & 0xFF; // 行车上电阶段 + int tChgCp = message[base + (706 - 530)] & 0xFF; // 慢充CP值 + int tChgR = message[base + (707 - 530)] & 0xFF; // 慢充枪阻值 + int tChgSts = message[base + (708 - 530)] & 0xFF; // 慢充阶段 + int fChgSts2 = message[base + (709 - 530)] & 0xFF; // 快充阶段 + int chgSts = message[base + (710 - 530)] & 0xFF; // 充电阶段 + int chgTime = ((message[base + (711 - 530)] & 0xFF) << 8) | (message[base + (712 - 530)] & 0xFF); // 充电时长(min) + long mileage = ((message[base + (713 - 530)] & 0xFFL) << 24) | ((message[base + (714 - 530)] & 0xFFL) << 16) | ((message[base + (715 - 530)] & 0xFFL) << 8) | (message[base + (716 - 530)] & 0xFFL); // 里程(u32) + log.info("设备 {} 扩展数据: resetCount={}, insulationValKOhm={}, carSpeedKmH={}, GPV0_1V={}, RTPs(V)=[{},{},{},{},{},{},{}], RTNs=[{},{},{},{}], realSOCMax0_1={}, realSOCMin0_1={}, chargeSeatTempsC=[{},{},{},{},{},{},{},{}]", deviceSn, resetCount, insulationVal, carSpd, gpv, rtp1, rtp2, rtp3, rtp4, rtp5, rtp6, rtp7, rtn1, rtn2, rtn3, rtn4, realSocMax, realSocMin, chgBs1Tmp, chgBs2Tmp, chgBs3Tmp, chgBs4Tmp, chgBs5Tmp, chgBs6Tmp, chgBs7Tmp, chgBs8Tmp); + log.info("设备 {} 充电详情: tChgCurA={}, tChgVolV={}, fChgCurA={}, fChgVolV={}, disSOP(A)=[{},{},{},{}], regSOP(A)=[{},{},{},{}], disSopTolA={}, regSopTolA={}", deviceSn, tChgCur, tChgVol, fChgCur, fChgVol, disSop10, disSop30, disSop60, disSopCont, regSop10, regSop30, regSop60, regSopCont, disSopTol, regSopTol); + log.info("设备 {} 版本与编码: hwVer={}, swVer={}, BMSCode='{}', VIN='{}', clientCode='{}', devId={}", deviceSn, hwVer, swVer, bmsCode, vin, clientCode, devId); + log.info("设备 {} 系统状态: sysVolV={}, sysCurA={}, cellCount={}, tempSensorCount={}, relayStatus={}, onaaccStatus={}, acStopCode={}, dcStopCode={}, cycleCount={}, samp25V={}, samp5V={}, chargeSopA={}, acChargeSopA={}", deviceSn, sysVol, sysCur, cellCount, tempSensorCount, relayStatus, onaaccStatus, acStopCode, dcStopCode, cycleCount, samp25v, samp5v, chargeSop, acChargeSop); + log.info("设备 {} 运行阶段: runSts={}, runPwrSts={}, tChgCp={}, tChgR={}, tChgSts={}, fChgSts={}, chgSts={}, chgTimeMin={}, mileage={}", deviceSn, runSts, runPwrSts, tChgCp, tChgR, tChgSts, fChgSts2, chgSts, chgTime, mileage); + } catch (Exception e) { + log.error("设备 {} 解析扩展数据时发生错误: {}", deviceSn, e.getMessage()); + } + } + + /** + * 读取 ASCII 字符串 + * @param startAbs BatteryData 绝对索引(例如 602 对应 BMS编码起始) + * @param len 字节长度 + * @param base 530 的映射起点(message 中的 startIndex) + */ + private String readAsciiRange(byte[] message, int startAbs, int len, int base) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len; i++) { + int idx = base + (startAbs - 530) + i; + if (idx >= 0 && idx < message.length) { + int b = message[idx] & 0xFF; + if (b != 0) { + sb.append((char) b); + } + } + } + return sb.toString().trim(); + } /** * 根据设备唯一编号(deviceCode)查询系统设备表的SN码 diff --git a/src/main/java/com/evobms/project/bms/service/impl/BmsChargeDetailServiceImpl.java b/src/main/java/com/evobms/project/bms/service/impl/BmsChargeDetailServiceImpl.java new file mode 100644 index 0000000..2fb14d5 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/service/impl/BmsChargeDetailServiceImpl.java @@ -0,0 +1,18 @@ +package com.evobms.project.bms.service.impl; + +import com.evobms.project.bms.domain.BmsChargeDetail; +import com.evobms.project.bms.mapper.BmsChargeDetailMapper; +import com.evobms.project.bms.service.IBmsChargeDetailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class BmsChargeDetailServiceImpl implements IBmsChargeDetailService { + @Autowired + private BmsChargeDetailMapper mapper; + + @Override + public int insertBmsChargeDetail(BmsChargeDetail data) { + return mapper.insertBmsChargeDetail(data); + } +} diff --git a/src/main/java/com/evobms/project/bms/service/impl/BmsSopDataServiceImpl.java b/src/main/java/com/evobms/project/bms/service/impl/BmsSopDataServiceImpl.java new file mode 100644 index 0000000..778daf3 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/service/impl/BmsSopDataServiceImpl.java @@ -0,0 +1,18 @@ +package com.evobms.project.bms.service.impl; + +import com.evobms.project.bms.domain.BmsSopData; +import com.evobms.project.bms.mapper.BmsSopDataMapper; +import com.evobms.project.bms.service.IBmsSopDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class BmsSopDataServiceImpl implements IBmsSopDataService { + @Autowired + private BmsSopDataMapper mapper; + + @Override + public int insertBmsSopData(BmsSopData data) { + return mapper.insertBmsSopData(data); + } +} diff --git a/src/main/java/com/evobms/project/bms/service/impl/BmsSystemStatusServiceImpl.java b/src/main/java/com/evobms/project/bms/service/impl/BmsSystemStatusServiceImpl.java new file mode 100644 index 0000000..37e9da2 --- /dev/null +++ b/src/main/java/com/evobms/project/bms/service/impl/BmsSystemStatusServiceImpl.java @@ -0,0 +1,18 @@ +package com.evobms.project.bms.service.impl; + +import com.evobms.project.bms.domain.BmsSystemStatus; +import com.evobms.project.bms.mapper.BmsSystemStatusMapper; +import com.evobms.project.bms.service.IBmsSystemStatusService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class BmsSystemStatusServiceImpl implements IBmsSystemStatusService { + @Autowired + private BmsSystemStatusMapper mapper; + + @Override + public int insertBmsSystemStatus(BmsSystemStatus data) { + return mapper.insertBmsSystemStatus(data); + } +} diff --git a/src/main/java/com/evobms/project/iot/controller/IotAuthController.java b/src/main/java/com/evobms/project/iot/controller/IotAuthController.java index a87144b..a9581d1 100644 --- a/src/main/java/com/evobms/project/iot/controller/IotAuthController.java +++ b/src/main/java/com/evobms/project/iot/controller/IotAuthController.java @@ -13,6 +13,7 @@ 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 lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -52,7 +53,9 @@ import java.util.Map; */ @RestController @Tag(name = "设备鉴权", description = "设备鉴权令牌接口") +@Api(tags = "IoT设备鉴权") @RequestMapping("/iot") +@Slf4j public class IotAuthController { private static final Logger log = LoggerFactory.getLogger(IotAuthController.class); @@ -96,11 +99,13 @@ public class IotAuthController { */ @PostMapping("/auth/token") @Operation(summary = "设备请求令牌", description = "通过SN码,设备号,版本信息生成TOKEN") + @ApiOperation("设备请求令牌") public Map requestToken(@RequestBody Map body) { String sn = safeTrim(body.get("sn")); String model = safeTrim(body.get("device")); String version = safeTrim(body.get("version")); - + log.info("设备请求:{}",body.toString()); + log.info("sn:{}, device:{}, version:{}", sn, model, version); MDC.put("sn", sn); MDC.put("model", model); MDC.put("version", version); @@ -388,6 +393,7 @@ public class IotAuthController { */ @GetMapping("/ota/{num}") @Operation(summary = "OTA固件分片下载", description = " OTA 固件分片下载接口") + @ApiOperation("OTA固件分片下载") public Object downloadOtaChunk(@PathVariable("num") String num, @RequestParam(value = "version", required = false) String version, HttpServletRequest request) { diff --git a/src/main/java/com/evobms/project/iot/controller/IotOtaController.java b/src/main/java/com/evobms/project/iot/controller/IotOtaController.java index fe980a1..a73c49f 100644 --- a/src/main/java/com/evobms/project/iot/controller/IotOtaController.java +++ b/src/main/java/com/evobms/project/iot/controller/IotOtaController.java @@ -3,6 +3,8 @@ package com.evobms.project.iot.controller; import com.evobms.common.constant.BboxApiConstants; import com.evobms.common.utils.StringUtils; import com.evobms.project.iot.service.DeviceTokenService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -25,6 +27,7 @@ import java.util.Map; */ @RestController @RequestMapping("/iot/ota") +@Api(tags = "IoT OTA状态上报") public class IotOtaController { private static final Logger log = LoggerFactory.getLogger(IotOtaController.class); @@ -32,6 +35,7 @@ public class IotOtaController { private DeviceTokenService deviceTokenService; @PostMapping("/status") + @ApiOperation("上报OTA升级状态") public Map reportOtaStatus(@RequestBody Map body, HttpServletRequest request) { Map resp = new HashMap<>(); diff --git a/src/main/java/com/evobms/project/ota/controller/OtaTasksController.java b/src/main/java/com/evobms/project/ota/controller/OtaTasksController.java index cbb5f3c..fbe4e13 100644 --- a/src/main/java/com/evobms/project/ota/controller/OtaTasksController.java +++ b/src/main/java/com/evobms/project/ota/controller/OtaTasksController.java @@ -20,6 +20,8 @@ import com.evobms.framework.web.controller.BaseController; import com.evobms.framework.web.domain.AjaxResult; import com.evobms.common.utils.poi.ExcelUtil; import com.evobms.framework.web.page.TableDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; /** * OTA任务Controller @@ -29,6 +31,7 @@ import com.evobms.framework.web.page.TableDataInfo; */ @RestController @RequestMapping("/ota/tasks") +@Api(tags = "OTA任务管理") public class OtaTasksController extends BaseController { @Autowired @@ -39,6 +42,7 @@ public class OtaTasksController extends BaseController */ //@PreAuthorize("@ss.hasPermi('OTA:tasks:list')") @GetMapping("/list") + @ApiOperation("查询OTA任务列表") public TableDataInfo list(OtaTasks otaTasks) { startPage(); @@ -52,6 +56,7 @@ public class OtaTasksController extends BaseController //@PreAuthorize("@ss.hasPermi('OTA:tasks:export')") @Log(title = "OTA任务", businessType = BusinessType.EXPORT) @PostMapping("/export") + @ApiOperation("导出OTA任务列表") public void export(HttpServletResponse response, OtaTasks otaTasks) { List list = otaTasksService.selectOtaTasksList(otaTasks); @@ -64,6 +69,7 @@ public class OtaTasksController extends BaseController */ //@PreAuthorize("@ss.hasPermi('OTA:tasks:query')") @GetMapping(value = "/{id}") + @ApiOperation("获取OTA任务详细信息") public AjaxResult getInfo(@PathVariable("id") String id) { return success(otaTasksService.selectOtaTasksById(id)); @@ -75,6 +81,7 @@ public class OtaTasksController extends BaseController //@PreAuthorize("@ss.hasPermi('OTA:tasks:add')") @Log(title = "OTA任务", businessType = BusinessType.INSERT) @PostMapping + @ApiOperation("新增OTA任务") public AjaxResult add(@RequestBody OtaTasks otaTasks) { return toAjax(otaTasksService.insertOtaTasks(otaTasks)); @@ -86,6 +93,7 @@ public class OtaTasksController extends BaseController //@PreAuthorize("@ss.hasPermi('OTA:tasks:edit')") @Log(title = "OTA任务", businessType = BusinessType.UPDATE) @PutMapping + @ApiOperation("修改OTA任务") public AjaxResult edit(@RequestBody OtaTasks otaTasks) { return toAjax(otaTasksService.updateOtaTasks(otaTasks)); @@ -96,6 +104,7 @@ public class OtaTasksController extends BaseController */ @Log(title = "OTA任务", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") + @ApiOperation("删除OTA任务") public AjaxResult remove(@PathVariable String[] ids) { return toAjax(otaTasksService.deleteOtaTasksByIds(ids)); diff --git a/src/main/java/com/evobms/project/ota/domain/OtaTasks.java b/src/main/java/com/evobms/project/ota/domain/OtaTasks.java index f6c8310..fc59acc 100644 --- a/src/main/java/com/evobms/project/ota/domain/OtaTasks.java +++ b/src/main/java/com/evobms/project/ota/domain/OtaTasks.java @@ -1,7 +1,11 @@ package com.evobms.project.ota.domain; import java.util.Date; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.evobms.framework.aspectj.lang.annotation.Excel; @@ -9,157 +13,62 @@ import com.evobms.framework.web.domain.BaseEntity; /** * OTA任务对象 ota_tasks - * + * * @author ruoyi * @date 2025-10-14 */ +@EqualsAndHashCode(callSuper = true) +@ApiModel(description = "OTA任务对象") +@Data public class OtaTasks extends BaseEntity { private static final long serialVersionUID = 1L; /** 任务ID(UUID) */ + @ApiModelProperty(value = "任务ID(UUID)") private String id; /** 设备ID */ + @ApiModelProperty(value = "设备ID") @Excel(name = "设备ID") private String deviceId; /** 任务名称 */ + @ApiModelProperty(value = "任务名称") @Excel(name = "任务名称") private String taskName; /** 目标固件版本 */ + @ApiModelProperty(value = "目标固件版本") @Excel(name = "目标固件版本") private String firmwareVersion; /** 任务状态 */ + @ApiModelProperty(value = "任务状态") @Excel(name = "任务状态") private String status; /** 升级进度(0-100) */ + @ApiModelProperty(value = "升级进度(0-100)") @Excel(name = "升级进度(0-100)") private Long progress; /** 开始时间 */ + @ApiModelProperty(value = "开始时间") @JsonFormat(pattern = "yyyy-MM-dd") @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd") private Date startTime; /** 结束时间 */ + @ApiModelProperty(value = "结束时间") @JsonFormat(pattern = "yyyy-MM-dd") @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd") private Date endTime; /** 错误信息 */ + @ApiModelProperty(value = "错误信息") @Excel(name = "错误信息") private String errorMessage; - public void setId(String id) - { - this.id = id; - } - public String getId() - { - return id; - } - - public void setDeviceId(String deviceId) - { - this.deviceId = deviceId; - } - - public String getDeviceId() - { - return deviceId; - } - - public void setTaskName(String taskName) - { - this.taskName = taskName; - } - - public String getTaskName() - { - return taskName; - } - - public void setFirmwareVersion(String firmwareVersion) - { - this.firmwareVersion = firmwareVersion; - } - - public String getFirmwareVersion() - { - return firmwareVersion; - } - - public void setStatus(String status) - { - this.status = status; - } - - public String getStatus() - { - return status; - } - - public void setProgress(Long progress) - { - this.progress = progress; - } - - public Long getProgress() - { - return progress; - } - - public void setStartTime(Date startTime) - { - this.startTime = startTime; - } - - public Date getStartTime() - { - return startTime; - } - - public void setEndTime(Date endTime) - { - this.endTime = endTime; - } - - public Date getEndTime() - { - return endTime; - } - - public void setErrorMessage(String errorMessage) - { - this.errorMessage = errorMessage; - } - - public String getErrorMessage() - { - return errorMessage; - } - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("id", getId()) - .append("deviceId", getDeviceId()) - .append("taskName", getTaskName()) - .append("firmwareVersion", getFirmwareVersion()) - .append("status", getStatus()) - .append("progress", getProgress()) - .append("startTime", getStartTime()) - .append("endTime", getEndTime()) - .append("errorMessage", getErrorMessage()) - .append("createTime", getCreateTime()) - .append("updateTime", getUpdateTime()) - .append("updateBy", getUpdateBy()) - .append("createBy", getCreateBy()) - .toString(); - } } diff --git a/src/main/java/com/evobms/project/summary/controller/ChargeDischargeSummaryController.java b/src/main/java/com/evobms/project/summary/controller/ChargeDischargeSummaryController.java index db34d65..2632de4 100644 --- a/src/main/java/com/evobms/project/summary/controller/ChargeDischargeSummaryController.java +++ b/src/main/java/com/evobms/project/summary/controller/ChargeDischargeSummaryController.java @@ -20,6 +20,8 @@ import com.evobms.framework.web.controller.BaseController; import com.evobms.framework.web.domain.AjaxResult; import com.evobms.common.utils.poi.ExcelUtil; import com.evobms.framework.web.page.TableDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; /** * 累计充放电量Controller @@ -29,6 +31,7 @@ import com.evobms.framework.web.page.TableDataInfo; */ @RestController @RequestMapping("/summary/summary") +@Api(tags = "充放电统计") public class ChargeDischargeSummaryController extends BaseController { @Autowired @@ -39,6 +42,7 @@ public class ChargeDischargeSummaryController extends BaseController */ @PreAuthorize("@ss.hasPermi('summary:summary:list')") @GetMapping("/list") + @ApiOperation("查询累计充放电量列表") public TableDataInfo list(ChargeDischargeSummary chargeDischargeSummary) { startPage(); @@ -52,6 +56,7 @@ public class ChargeDischargeSummaryController extends BaseController @PreAuthorize("@ss.hasPermi('summary:summary:export')") @Log(title = "累计充放电量", businessType = BusinessType.EXPORT) @PostMapping("/export") + @ApiOperation("导出累计充放电量列表") public void export(HttpServletResponse response, ChargeDischargeSummary chargeDischargeSummary) { List list = chargeDischargeSummaryService.selectChargeDischargeSummaryList(chargeDischargeSummary); @@ -64,6 +69,7 @@ public class ChargeDischargeSummaryController extends BaseController */ @PreAuthorize("@ss.hasPermi('summary:summary:query')") @GetMapping(value = "/{id}") + @ApiOperation("获取累计充放电量详细信息") public AjaxResult getInfo(@PathVariable("id") Long id) { return success(chargeDischargeSummaryService.selectChargeDischargeSummaryById(id)); @@ -75,6 +81,7 @@ public class ChargeDischargeSummaryController extends BaseController @PreAuthorize("@ss.hasPermi('summary:summary:add')") @Log(title = "累计充放电量", businessType = BusinessType.INSERT) @PostMapping + @ApiOperation("新增累计充放电量") public AjaxResult add(@RequestBody ChargeDischargeSummary chargeDischargeSummary) { return toAjax(chargeDischargeSummaryService.insertChargeDischargeSummary(chargeDischargeSummary)); @@ -86,6 +93,7 @@ public class ChargeDischargeSummaryController extends BaseController @PreAuthorize("@ss.hasPermi('summary:summary:edit')") @Log(title = "累计充放电量", businessType = BusinessType.UPDATE) @PutMapping + @ApiOperation("修改累计充放电量") public AjaxResult edit(@RequestBody ChargeDischargeSummary chargeDischargeSummary) { return toAjax(chargeDischargeSummaryService.updateChargeDischargeSummary(chargeDischargeSummary)); @@ -96,7 +104,8 @@ public class ChargeDischargeSummaryController extends BaseController */ @PreAuthorize("@ss.hasPermi('summary:summary:remove')") @Log(title = "累计充放电量", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") + @DeleteMapping("/{ids}") + @ApiOperation("删除累计充放电量") public AjaxResult remove(@PathVariable Long[] ids) { return toAjax(chargeDischargeSummaryService.deleteChargeDischargeSummaryByIds(ids)); diff --git a/src/main/java/com/evobms/project/summary/domain/ChargeDischargeSummary.java b/src/main/java/com/evobms/project/summary/domain/ChargeDischargeSummary.java index 0ff9230..658ac62 100644 --- a/src/main/java/com/evobms/project/summary/domain/ChargeDischargeSummary.java +++ b/src/main/java/com/evobms/project/summary/domain/ChargeDischargeSummary.java @@ -3,6 +3,10 @@ package com.evobms.project.summary.domain; import java.math.BigDecimal; import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.evobms.framework.aspectj.lang.annotation.Excel; @@ -14,6 +18,9 @@ import com.evobms.framework.web.domain.BaseEntity; * @author 田志阳 * @date 2026-01-09 */ +@Data +@EqualsAndHashCode(callSuper = true) +@ApiModel(value = "ChargeDischargeSummary", description = "累计充放电量对象") public class ChargeDischargeSummary extends BaseEntity { private static final long serialVersionUID = 1L; @@ -23,83 +30,23 @@ public class ChargeDischargeSummary extends BaseEntity /** 设备ID */ @Excel(name = "设备ID") + @ApiModelProperty(value = "设备ID") private String deviceId; /** 时间戳 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss ") + @ApiModelProperty(value = "时间戳") @Excel(name = "时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date timestamp; /** 累计放电(kWh) */ + @ApiModelProperty(value = "累计放电(kWh)") @Excel(name = "累计放电(kWh)") private BigDecimal dischargeKwh; /** 累计充电kWh) */ - @Excel(name = "累计充电kWh)") + @ApiModelProperty(value = "累计充电kWh") + @Excel(name = "累计充电(kWh)") private BigDecimal chargeKwh; - 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 setDischargeKwh(BigDecimal dischargeKwh) - { - this.dischargeKwh = dischargeKwh; - } - - public BigDecimal getDischargeKwh() - { - return dischargeKwh; - } - - public void setChargeKwh(BigDecimal chargeKwh) - { - this.chargeKwh = chargeKwh; - } - - public BigDecimal getChargeKwh() - { - return chargeKwh; - } - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("id", getId()) - .append("deviceId", getDeviceId()) - .append("timestamp", getTimestamp()) - .append("dischargeKwh", getDischargeKwh()) - .append("chargeKwh", getChargeKwh()) - .append("createTime", getCreateTime()) - .append("updateTime", getUpdateTime()) - .append("createBy", getCreateBy()) - .append("updateBy", getUpdateBy()) - .toString(); - } } diff --git a/src/main/java/com/evobms/project/system/controller/BmsDevicesController.java b/src/main/java/com/evobms/project/system/controller/BmsDevicesController.java index a2dd460..fb194b8 100644 --- a/src/main/java/com/evobms/project/system/controller/BmsDevicesController.java +++ b/src/main/java/com/evobms/project/system/controller/BmsDevicesController.java @@ -20,15 +20,18 @@ import com.evobms.framework.web.controller.BaseController; import com.evobms.framework.web.domain.AjaxResult; import com.evobms.common.utils.poi.ExcelUtil; import com.evobms.framework.web.page.TableDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; /** * BBOX管理Controller * * @author 田志阳 - * @date 2025-10-10 + * @date 2025-11-15 */ @RestController @RequestMapping("/devices/devices") +@Api(tags = "BBOX设备管理") public class BmsDevicesController extends BaseController { @Autowired @@ -37,7 +40,9 @@ public class BmsDevicesController extends BaseController /** * 查询BBOX管理列表 */ + @PreAuthorize("@ss.hasPermi('system:devices:list')") @GetMapping("/list") + @ApiOperation("查询BBOX设备列表") public TableDataInfo list(BmsDevices bmsDevices) { startPage(); @@ -48,8 +53,10 @@ public class BmsDevicesController extends BaseController /** * 导出BBOX管理列表 */ + @PreAuthorize("@ss.hasPermi('system:devices:export')") @Log(title = "BBOX管理", businessType = BusinessType.EXPORT) @PostMapping("/export") + @ApiOperation("导出BBOX设备列表") public void export(HttpServletResponse response, BmsDevices bmsDevices) { List list = bmsDevicesService.selectBmsDevicesList(bmsDevices); @@ -60,7 +67,9 @@ public class BmsDevicesController extends BaseController /** * 获取BBOX管理详细信息 */ + @PreAuthorize("@ss.hasPermi('system:devices:query')") @GetMapping(value = "/{id}") + @ApiOperation("获取BBOX设备详细信息") public AjaxResult getInfo(@PathVariable("id") String id) { return success(bmsDevicesService.selectBmsDevicesById(id)); @@ -69,8 +78,10 @@ public class BmsDevicesController extends BaseController /** * 新增BBOX管理 */ + @PreAuthorize("@ss.hasPermi('system:devices:add')") @Log(title = "BBOX管理", businessType = BusinessType.INSERT) @PostMapping + @ApiOperation("新增BBOX设备") public AjaxResult add(@RequestBody BmsDevices bmsDevices) { return toAjax(bmsDevicesService.insertBmsDevices(bmsDevices)); @@ -79,8 +90,10 @@ public class BmsDevicesController extends BaseController /** * 修改BBOX管理 */ + @PreAuthorize("@ss.hasPermi('system:devices:edit')") @Log(title = "BBOX管理", businessType = BusinessType.UPDATE) @PutMapping + @ApiOperation("修改BBOX设备") public AjaxResult edit(@RequestBody BmsDevices bmsDevices) { return toAjax(bmsDevicesService.updateBmsDevices(bmsDevices)); @@ -89,8 +102,10 @@ public class BmsDevicesController extends BaseController /** * 删除BBOX管理 */ + @PreAuthorize("@ss.hasPermi('system:devices:remove')") @Log(title = "BBOX管理", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") + @DeleteMapping("/{ids}") + @ApiOperation("删除BBOX设备") public AjaxResult remove(@PathVariable String[] ids) { return toAjax(bmsDevicesService.deleteBmsDevicesByIds(ids)); diff --git a/src/main/java/com/evobms/project/system/domain/BmsDevices.java b/src/main/java/com/evobms/project/system/domain/BmsDevices.java index bc220ff..3c3acf8 100644 --- a/src/main/java/com/evobms/project/system/domain/BmsDevices.java +++ b/src/main/java/com/evobms/project/system/domain/BmsDevices.java @@ -2,6 +2,8 @@ package com.evobms.project.system.domain; import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -17,41 +19,51 @@ import com.evobms.framework.web.domain.BaseEntity; */ @EqualsAndHashCode(callSuper = true) @Data +@ApiModel(value = "BmsDevices", description = "BBOX设备管理对象") public class BmsDevices extends BaseEntity { private static final long serialVersionUID = 1L; /** 主键ID */ + @ApiModelProperty("主键ID") private String id; /** 设备唯一编号 */ + @ApiModelProperty("设备唯一编号") @Excel(name = "设备唯一编号") private String deviceId; /** 设备名称 */ + @ApiModelProperty("设备名称") @Excel(name = "设备名称") private String deviceName; /** 设备名称 */ + @ApiModelProperty("设备SN码") @Excel(name = "设备SN码") private String deviceSn; /** 设备类型 */ + @ApiModelProperty("设备类型") @Excel(name = "设备类型") private String deviceType; /** 设备状态 */ + @ApiModelProperty("设备状态") @Excel(name = "设备状态") private String status; /** 设备IP地址 */ + @ApiModelProperty("设备IP地址") @Excel(name = "设备IP地址") private String ipAddress; /** 服务器固件版本号 */ + @ApiModelProperty("固件版本号") @Excel(name = "固件版本号") private String firmwareVersion; /** 最后在线时间 */ + @ApiModelProperty("最后在线时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "最后在线时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date lastOnline; diff --git a/src/main/java/com/evobms/project/vehicledata/controller/BatteryChartController.java b/src/main/java/com/evobms/project/vehicledata/controller/BatteryChartController.java index 06efb22..f6b36e2 100644 --- a/src/main/java/com/evobms/project/vehicledata/controller/BatteryChartController.java +++ b/src/main/java/com/evobms/project/vehicledata/controller/BatteryChartController.java @@ -29,6 +29,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; /** * 图标组件Controller * @@ -57,7 +58,9 @@ public class BatteryChartController { // 图表数据:按最近 hours 小时返回整车电压/电流/SOC 的时间序列 @GetMapping("/battery-data/{deviceId}/chart") @ApiOperation(value = "整车电压电流SOC时间序列", notes = "按最近hours小时返回时间序列") - public List getChartData(@PathVariable("deviceId") String deviceId, @RequestParam(value = "hours", required = false, defaultValue = "24") int hours) { + public List getChartData( + @ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, + @ApiParam(value = "最近多少小时的数据", defaultValue = "24") @RequestParam(value = "hours", required = false, defaultValue = "24") int hours) { long millis = System.currentTimeMillis() - (long) hours * 3600_000L; Date startTime = new Date(millis); List rows = vehicleDataService.selectByDeviceAndStartTime(deviceId, startTime); @@ -72,21 +75,25 @@ public class BatteryChartController { // 整车最新:返回 deviceId 最新的一条整车数据 @GetMapping("/vehicle-data/{deviceId}/latest") @ApiOperation(value = "整车最新数据", notes = "返回指定设备最新整车数据") - public VehicleData latestVehicleData(@PathVariable("deviceId") String deviceId) { + public VehicleData latestVehicleData(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId) { return vehicleDataService.selectLatestByDeviceId(deviceId); } // 位置最新:返回 deviceId 最新的一条位置数据 @GetMapping("/vehicle-location/{deviceId}/latest") @ApiOperation(value = "车辆最新位置", notes = "返回指定设备最新位置数据") - public VehicleLocation latestVehicleLocation(@PathVariable("deviceId") String deviceId) { + public VehicleLocation latestVehicleLocation(@ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId) { return vehicleLocationService.selectLatestByDeviceId(deviceId); } // 位置轨迹:按时间范围返回位置列表,start/end 为毫秒时间戳,可选 limit 限制条数 @GetMapping("/vehicle-location/{deviceId}") @ApiOperation(value = "车辆位置轨迹", notes = "按时间范围返回位置列表") - public List locationRange(@PathVariable("deviceId") String deviceId, @RequestParam("start") long startMillis, @RequestParam("end") long endMillis, @RequestParam(value = "limit", required = false) Integer limit) { + public List locationRange( + @ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, + @ApiParam(value = "开始时间戳(毫秒)", required = true) @RequestParam("start") long startMillis, + @ApiParam(value = "结束时间戳(毫秒)", required = true) @RequestParam("end") long endMillis, + @ApiParam(value = "限制返回条数") @RequestParam(value = "limit", required = false) Integer limit) { Date start = new Date(startMillis); Date end = new Date(endMillis); return vehicleLocationService.selectByDeviceAndRange(deviceId, start, end, limit); @@ -95,21 +102,26 @@ public class BatteryChartController { // 子系统最新帧(电压):返回指定子系统号最新的一帧电压数据 @GetMapping("/subsystem-voltage/{deviceId}/{subsystemNo}/latest") @ApiOperation(value = "子系统最新电压帧", notes = "返回指定子系统最新电压数据") - public SubsystemVoltage latestVoltage(@PathVariable("deviceId") String deviceId, @PathVariable("subsystemNo") Integer subsystemNo) { + public SubsystemVoltage latestVoltage( + @ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, + @ApiParam(value = "子系统号", required = true) @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) { + public SubsystemTemperature latestTemperature( + @ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, + @ApiParam(value = "子系统号", required = true) @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) { + public ExtremeValues latestExtremeValues( + @ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String deviceId, + @ApiParam(value = "筛选指定时间之前的数据(yyyy-MM-dd HH:mm:ss 或 毫秒时间戳)") @RequestParam(value = "before", required = false) String before) { if (before == null || before.isEmpty()) { return extremeValuesService.selectLatestByDeviceId(deviceId); } @@ -126,7 +138,7 @@ public class BatteryChartController { return extremeValuesService.selectLatestByDeviceIdBefore(deviceId, beforeTime); } @GetMapping("/api/dashboard/home") - @ApiOperation(value = "首页所有分布数据", notes = "返回指定设备极值数据,支持before筛选") + @ApiOperation(value = "首页所有分布数据", notes = "获取首页大屏的分布统计数据") public R home(){ HomeDashboardDTO dto = vehicleDataService.getHomeDashboard(); return R.ok(dto); diff --git a/src/main/java/com/evobms/project/vehicledata/controller/VehicleDataController.java b/src/main/java/com/evobms/project/vehicledata/controller/VehicleDataController.java index c66f55d..ac08f46 100644 --- a/src/main/java/com/evobms/project/vehicledata/controller/VehicleDataController.java +++ b/src/main/java/com/evobms/project/vehicledata/controller/VehicleDataController.java @@ -20,6 +20,8 @@ import com.evobms.framework.web.controller.BaseController; import com.evobms.framework.web.domain.AjaxResult; import com.evobms.common.utils.poi.ExcelUtil; import com.evobms.framework.web.page.TableDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; /** * 整车数据Controller @@ -29,6 +31,7 @@ import com.evobms.framework.web.page.TableDataInfo; */ @RestController @RequestMapping("/VehicleData/VehicleData") +@Api(tags = "整车数据管理") public class VehicleDataController extends BaseController { @Autowired @@ -39,6 +42,7 @@ public class VehicleDataController extends BaseController */ @PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:list')") @GetMapping("/list") + @ApiOperation("查询整车数据列表") public TableDataInfo list(VehicleData vehicleData) { startPage(); @@ -52,6 +56,7 @@ public class VehicleDataController extends BaseController @PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:export')") @Log(title = "整车数据", businessType = BusinessType.EXPORT) @PostMapping("/export") + @ApiOperation("导出整车数据列表") public void export(HttpServletResponse response, VehicleData vehicleData) { List list = vehicleDataService.selectVehicleDataList(vehicleData); @@ -64,6 +69,7 @@ public class VehicleDataController extends BaseController */ @PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:query')") @GetMapping(value = "/{id}") + @ApiOperation("获取整车数据详细信息") public AjaxResult getInfo(@PathVariable("id") Long id) { return success(vehicleDataService.selectVehicleDataById(id)); @@ -75,6 +81,7 @@ public class VehicleDataController extends BaseController @PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:add')") @Log(title = "整车数据", businessType = BusinessType.INSERT) @PostMapping + @ApiOperation("新增整车数据") public AjaxResult add(@RequestBody VehicleData vehicleData) { return toAjax(vehicleDataService.insertVehicleData(vehicleData)); @@ -86,6 +93,7 @@ public class VehicleDataController extends BaseController @PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:edit')") @Log(title = "整车数据", businessType = BusinessType.UPDATE) @PutMapping + @ApiOperation("修改整车数据") public AjaxResult edit(@RequestBody VehicleData vehicleData) { return toAjax(vehicleDataService.updateVehicleData(vehicleData)); @@ -96,7 +104,8 @@ public class VehicleDataController extends BaseController */ @PreAuthorize("@ss.hasPermi('VehicleData:VehicleData:remove')") @Log(title = "整车数据", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") + @DeleteMapping("/{ids}") + @ApiOperation("删除整车数据") public AjaxResult remove(@PathVariable Long[] ids) { return toAjax(vehicleDataService.deleteVehicleDataByIds(ids)); diff --git a/src/main/java/com/evobms/project/vehicledata/controller/VehicleLocationController.java b/src/main/java/com/evobms/project/vehicledata/controller/VehicleLocationController.java index b211ff2..fbbdbcb 100644 --- a/src/main/java/com/evobms/project/vehicledata/controller/VehicleLocationController.java +++ b/src/main/java/com/evobms/project/vehicledata/controller/VehicleLocationController.java @@ -20,6 +20,8 @@ import com.evobms.framework.web.controller.BaseController; import com.evobms.framework.web.domain.AjaxResult; import com.evobms.common.utils.poi.ExcelUtil; import com.evobms.framework.web.page.TableDataInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; /** * 车辆位置数据Controller @@ -29,6 +31,7 @@ import com.evobms.framework.web.page.TableDataInfo; */ @RestController @RequestMapping("/location/location") +@Api(tags = "车辆位置管理") public class VehicleLocationController extends BaseController { @Autowired @@ -39,6 +42,7 @@ public class VehicleLocationController extends BaseController */ @PreAuthorize("@ss.hasPermi('location:location:list')") @GetMapping("/list") + @ApiOperation("查询车辆位置列表") public TableDataInfo list(VehicleLocation vehicleLocation) { startPage(); @@ -52,6 +56,7 @@ public class VehicleLocationController extends BaseController @PreAuthorize("@ss.hasPermi('location:location:export')") @Log(title = "车辆位置数据", businessType = BusinessType.EXPORT) @PostMapping("/export") + @ApiOperation("导出车辆位置列表") public void export(HttpServletResponse response, VehicleLocation vehicleLocation) { List list = vehicleLocationService.selectVehicleLocationList(vehicleLocation); @@ -64,6 +69,7 @@ public class VehicleLocationController extends BaseController */ @PreAuthorize("@ss.hasPermi('location:location:query')") @GetMapping(value = "/{id}") + @ApiOperation("获取车辆位置详细信息") public AjaxResult getInfo(@PathVariable("id") Long id) { return success(vehicleLocationService.selectVehicleLocationById(id)); @@ -75,6 +81,7 @@ public class VehicleLocationController extends BaseController @PreAuthorize("@ss.hasPermi('location:location:add')") @Log(title = "车辆位置数据", businessType = BusinessType.INSERT) @PostMapping + @ApiOperation("新增车辆位置") public AjaxResult add(@RequestBody VehicleLocation vehicleLocation) { return toAjax(vehicleLocationService.insertVehicleLocation(vehicleLocation)); @@ -86,6 +93,7 @@ public class VehicleLocationController extends BaseController @PreAuthorize("@ss.hasPermi('location:location:edit')") @Log(title = "车辆位置数据", businessType = BusinessType.UPDATE) @PutMapping + @ApiOperation("修改车辆位置") public AjaxResult edit(@RequestBody VehicleLocation vehicleLocation) { return toAjax(vehicleLocationService.updateVehicleLocation(vehicleLocation)); @@ -96,7 +104,8 @@ public class VehicleLocationController extends BaseController */ @PreAuthorize("@ss.hasPermi('location:location:remove')") @Log(title = "车辆位置数据", businessType = BusinessType.DELETE) - @DeleteMapping("/{ids}") + @DeleteMapping("/{ids}") + @ApiOperation("删除车辆位置") public AjaxResult remove(@PathVariable Long[] ids) { return toAjax(vehicleLocationService.deleteVehicleLocationByIds(ids)); diff --git a/src/main/java/com/evobms/project/vehicledata/domain/HomeDashboardDTO.java b/src/main/java/com/evobms/project/vehicledata/domain/HomeDashboardDTO.java index d28cbf5..e3962a2 100644 --- a/src/main/java/com/evobms/project/vehicledata/domain/HomeDashboardDTO.java +++ b/src/main/java/com/evobms/project/vehicledata/domain/HomeDashboardDTO.java @@ -1,18 +1,28 @@ package com.evobms.project.vehicledata.domain; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.Map; @Data +@ApiModel(description = "首页仪表盘数据传输对象") public class HomeDashboardDTO { + @ApiModelProperty(value = "设备总数") private Long deviceCount; // 设备总数 + @ApiModelProperty(value = "在线设备数") private Integer onlineCount; // 在线设备数 + @ApiModelProperty(value = "平均 SOC") private Double avgSoc; // 平均 SOC + @ApiModelProperty(value = "当前最高温度") private Double maxTemp; // 当前最高温度 + @ApiModelProperty(value = "最大单体压差") private Double maxVoltageDiff; // 最大单体压差 + @ApiModelProperty(value = "未恢复告警数") private Integer alarmCount; // 未恢复告警数 - + @ApiModelProperty(value = "SOC 分布") private Map socDistribution; // SOC 分布 + @ApiModelProperty(value = "温度分布") private Map tempDistribution; // 温度分布 } diff --git a/src/main/java/com/evobms/project/vehicledata/domain/VehicleData.java b/src/main/java/com/evobms/project/vehicledata/domain/VehicleData.java index 3fcba36..eda5c3f 100644 --- a/src/main/java/com/evobms/project/vehicledata/domain/VehicleData.java +++ b/src/main/java/com/evobms/project/vehicledata/domain/VehicleData.java @@ -2,7 +2,11 @@ package com.evobms.project.vehicledata.domain; import java.math.BigDecimal; import java.util.Date; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.evobms.framework.aspectj.lang.annotation.Excel; @@ -14,240 +18,87 @@ import com.evobms.framework.web.domain.BaseEntity; * @author 田志阳 * @date 2025-11-15 */ +@ApiModel(description = "整车数据对象") +@Data +@EqualsAndHashCode(callSuper = true) public class VehicleData extends BaseEntity { private static final long serialVersionUID = 1L; /** 主键ID */ + @ApiModelProperty(value = "主键ID") private Long id; /** 设备ID */ + @ApiModelProperty(value = "设备ID") @Excel(name = "设备ID") private String deviceId; /** 数据时间戳 */ + @ApiModelProperty(value = "数据时间戳") @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:无效) */ + @ApiModelProperty(value = "车辆状态 (1:启动 2:熄火 3:其他 254:异常 255:无效)") @Excel(name = "车辆状态 (1:启动 2:熄火 3:其他 254:异常 255:无效)") private Integer vehicleStatus; /** 充电状态 (1:停车充电 2:行驶充电 3:未充电 4:充电完成 254:异常 255:无效) */ + @ApiModelProperty(value = "充电状态 (1:停车充电 2:行驶充电 3:未充电 4:充电完成 254:异常 255:无效)") @Excel(name = "充电状态 (1:停车充电 2:行驶充电 3:未充电 4:充电完成 254:异常 255:无效)") private Integer chargingStatus; /** 运行模式 (1:纯电 2:混动 3:燃油 254:异常 255:无效) */ + @ApiModelProperty(value = "运行模式 (1:纯电 2:混动 3:燃油 254:异常 255:无效)") @Excel(name = "运行模式 (1:纯电 2:混动 3:燃油 254:异常 255:无效)") private Integer operationMode; /** 车速(km/h) */ + @ApiModelProperty(value = "车速(km/h)") @Excel(name = "车速(km/h)") private BigDecimal vehicleSpeed; /** 累计里程(km) */ + @ApiModelProperty(value = "累计里程(km)") @Excel(name = "累计里程(km)") private BigDecimal totalMileage; /** 总电压(V) */ + @ApiModelProperty(value = "总电压(V)") @Excel(name = "总电压(V)") private BigDecimal totalVoltage; /** 总电流(A) */ + @ApiModelProperty(value = "总电流(A)") @Excel(name = "总电流(A)") private BigDecimal totalCurrent; /** SOC电量百分比(%) */ + @ApiModelProperty(value = "SOC电量百分比(%)") @Excel(name = "SOC电量百分比(%)") private Integer soc; /** DC-DC状态 (1:工作 2:断开) */ + @ApiModelProperty(value = "DC-DC状态 (1:工作 2:断开)") @Excel(name = "DC-DC状态 (1:工作 2:断开)") private Integer dcdcStatus; /** 档位 (0:空档 1-6:1-6档 13:倒档 14:自动D档 15:停车P档) */ + @ApiModelProperty(value = "档位 (0:空档 1-6:1-6档 13:倒档 14:自动D档 15:停车P档)") @Excel(name = "档位 (0:空档 1-6:1-6档 13:倒档 14:自动D档 15:停车P档)") private Integer gearPosition; /** 绝缘电阻(kΩ) */ + @ApiModelProperty(value = "绝缘电阻(kΩ)") @Excel(name = "绝缘电阻(kΩ)") private Integer insulationResistance; /** 预留字段 */ + @ApiModelProperty(value = "预留字段") @Excel(name = "预留字段") private String reservedData; - 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 setVehicleStatus(Integer vehicleStatus) - { - this.vehicleStatus = vehicleStatus; - } - - public Integer getVehicleStatus() - { - return vehicleStatus; - } - - public void setChargingStatus(Integer chargingStatus) - { - this.chargingStatus = chargingStatus; - } - - public Integer getChargingStatus() - { - return chargingStatus; - } - - public void setOperationMode(Integer operationMode) - { - this.operationMode = operationMode; - } - - public Integer getOperationMode() - { - return operationMode; - } - - public void setVehicleSpeed(BigDecimal vehicleSpeed) - { - this.vehicleSpeed = vehicleSpeed; - } - - public BigDecimal getVehicleSpeed() - { - return vehicleSpeed; - } - - public void setTotalMileage(BigDecimal totalMileage) - { - this.totalMileage = totalMileage; - } - - public BigDecimal getTotalMileage() - { - return totalMileage; - } - - public void setTotalVoltage(BigDecimal totalVoltage) - { - this.totalVoltage = totalVoltage; - } - - public BigDecimal getTotalVoltage() - { - return totalVoltage; - } - - public void setTotalCurrent(BigDecimal totalCurrent) - { - this.totalCurrent = totalCurrent; - } - - public BigDecimal getTotalCurrent() - { - return totalCurrent; - } - - public void setSoc(Integer soc) - { - this.soc = soc; - } - - public Integer getSoc() - { - return soc; - } - - public void setDcdcStatus(Integer dcdcStatus) - { - this.dcdcStatus = dcdcStatus; - } - - public Integer getDcdcStatus() - { - return dcdcStatus; - } - - public void setGearPosition(Integer gearPosition) - { - this.gearPosition = gearPosition; - } - - public Integer getGearPosition() - { - return gearPosition; - } - - public void setInsulationResistance(Integer insulationResistance) - { - this.insulationResistance = insulationResistance; - } - - public Integer getInsulationResistance() - { - return insulationResistance; - } - - public void setReservedData(String reservedData) - { - this.reservedData = reservedData; - } - - public String getReservedData() - { - return reservedData; - } - - - @Override - public String toString() { - return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) - .append("id", getId()) - .append("deviceId", getDeviceId()) - .append("timestamp", getTimestamp()) - .append("vehicleStatus", getVehicleStatus()) - .append("chargingStatus", getChargingStatus()) - .append("operationMode", getOperationMode()) - .append("vehicleSpeed", getVehicleSpeed()) - .append("totalMileage", getTotalMileage()) - .append("totalVoltage", getTotalVoltage()) - .append("totalCurrent", getTotalCurrent()) - .append("soc", getSoc()) - .append("dcdcStatus", getDcdcStatus()) - .append("gearPosition", getGearPosition()) - .append("insulationResistance", getInsulationResistance()) - .append("reservedData", getReservedData()) - .toString(); - } } diff --git a/src/main/java/com/evobms/project/vehicledata/domain/VehicleLocation.java b/src/main/java/com/evobms/project/vehicledata/domain/VehicleLocation.java index 5f97e7a..780452a 100644 --- a/src/main/java/com/evobms/project/vehicledata/domain/VehicleLocation.java +++ b/src/main/java/com/evobms/project/vehicledata/domain/VehicleLocation.java @@ -3,7 +3,11 @@ package com.evobms.project.vehicledata.domain; import java.math.BigDecimal; import java.util.Date; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.evobms.framework.aspectj.lang.annotation.Excel; @@ -15,23 +19,29 @@ import com.evobms.framework.web.domain.BaseEntity; * @author ruoyi * @date 2025-11-15 */ +@ApiModel(description = "车辆位置数据对象") +@Data +@EqualsAndHashCode(callSuper = false) public class VehicleLocation extends BaseEntity { private static final long serialVersionUID = 1L; /** * 主键ID */ + @ApiModelProperty(value = "主键ID") private Long id; /** * 设备ID */ + @ApiModelProperty(value = "设备ID") @Excel(name = "设备ID") private String deviceId; /** * 数据时间戳 */ + @ApiModelProperty(value = "数据时间戳") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "数据时间戳", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date timestamp; @@ -39,79 +49,21 @@ public class VehicleLocation extends BaseEntity { /** * 定位状态 (0:无效 1:有效) */ + @ApiModelProperty(value = "定位状态 (0:无效 1:有效)") @Excel(name = "定位状态 (0:无效 1:有效)") private Integer positioningStatus; /** * 经度 */ + @ApiModelProperty(value = "经度") @Excel(name = "经度") private BigDecimal longitude; /** * 纬度 */ + @ApiModelProperty(value = "纬度") @Excel(name = "纬度") private BigDecimal latitude; - - 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 setPositioningStatus(Integer positioningStatus) { - this.positioningStatus = positioningStatus; - } - - public Integer getPositioningStatus() { - return positioningStatus; - } - - public void setLongitude(BigDecimal longitude) { - this.longitude = longitude; - } - - public BigDecimal getLongitude() { - return longitude; - } - - public void setLatitude(BigDecimal latitude) { - this.latitude = latitude; - } - - public BigDecimal getLatitude() { - return latitude; - } - - - @Override - public String toString() { - return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) - .append("id", getId()) - .append("deviceId", getDeviceId()) - .append("timestamp", getTimestamp()) - .append("positioningStatus", getPositioningStatus()) - .append("longitude", getLongitude()) - .append("latitude", getLatitude()) - .toString(); - } } diff --git a/src/main/resources/application-druid.yml b/src/main/resources/application-druid.yml index 3edbf21..0137c99 100644 --- a/src/main/resources/application-druid.yml +++ b/src/main/resources/application-druid.yml @@ -6,7 +6,7 @@ spring: druid: # 主库数据源 master: - url: jdbc:mysql://localhost:3306/evo_bms1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://192.168.5.121:3306/evo_bms1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: root # 从库数据源 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 675e2f7..30b9f09 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,7 +16,7 @@ ruoyi: # 开发环境配置 server: # 服务器HTTP端口,支持环境变量覆盖:SERVER_PORT,默认8080 - port: ${SERVER_PORT:8080} + port: ${SERVER_PORT:8011} servlet: # 应用的访问路径 context-path: / diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt index 0931cb8..0f0bab9 100644 --- a/src/main/resources/banner.txt +++ b/src/main/resources/banner.txt @@ -1,24 +1,10 @@ -Application Version: ${ruoyi.version} +Application Version: ${ruoyi-vue-plus.version} Spring Boot Version: ${spring-boot.version} -//////////////////////////////////////////////////////////////////// -// _ooOoo_ // -// o8888888o // -// 88" . "88 // -// (| ^_^ |) // -// O\ = /O // -// ____/`---'\____ // -// .' \\| |// `. // -// / \\||| : |||// \ // -// / _||||| -:- |||||- \ // -// | | \\\ - /// | | // -// | \_| ''\---/'' | | // -// \ .-\__ `-` ___/-. / // -// ___`. .' /--.--\ `. . ___ // -// ."" '< `.___\_<|>_/___.' >'"". // -// | | : `- \`.;`\ _ /`;.`/ - ` : | | // -// \ \ `-. \_ __\ /__ _/ .-` / / // -// ========`-.____`-.___\_____/___.-`____.-'======== // -// `=---=' // -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // -// 佛祖保佑 永不宕机 永无BUG // -//////////////////////////////////////////////////////////////////// \ No newline at end of file + _______ _______ _________ _______ _______ +( ____ \|\ /|( ___ )\__ __/( ____ \( ____ \|\ /| +| ( \/| ) ( || ( ) | ) ( | ( \/| ( \/| ) ( | +| (__ | | | || | | | | | | (__ | | | (___) | +| __) ( ( ) )| | | | | | | __) | | | ___ | +| ( \ \_/ / | | | | | | | ( | | | ( ) | +| (____/\ \ / | (___) | | | | (____/\| (____/\| ) ( | +(_______/ \_/ (_______) )_( (_______/(_______/|/ \| diff --git a/src/main/resources/mybatis/bms/BmsChargeDetailMapper.xml b/src/main/resources/mybatis/bms/BmsChargeDetailMapper.xml new file mode 100644 index 0000000..11f7eba --- /dev/null +++ b/src/main/resources/mybatis/bms/BmsChargeDetailMapper.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + insert into bms_charge_detail + + device_id, + timestamp, + ac_charge_current, + ac_charge_voltage, + dc_charge_current, + dc_charge_voltage, + charge_time, + ac_stop_code, + dc_stop_code, + ac_cp_value, + ac_gun_resistance, + charging_stage, + ac_stage, + dc_stage, + create_time, + update_time, + create_by, + update_by, + + + #{deviceId}, + #{timestamp}, + #{acChargeCurrent}, + #{acChargeVoltage}, + #{dcChargeCurrent}, + #{dcChargeVoltage}, + #{chargeTime}, + #{acStopCode}, + #{dcStopCode}, + #{acCpValue}, + #{acGunResistance}, + #{chargingStage}, + #{acStage}, + #{dcStage}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + + + diff --git a/src/main/resources/mybatis/bms/BmsSopDataMapper.xml b/src/main/resources/mybatis/bms/BmsSopDataMapper.xml new file mode 100644 index 0000000..5c4904c --- /dev/null +++ b/src/main/resources/mybatis/bms/BmsSopDataMapper.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + insert into bms_sop_data + + device_id, + timestamp, + discharge_sop_10s, + discharge_sop_30s, + discharge_sop_60s, + discharge_sop_continuous, + regen_sop_10s, + regen_sop_30s, + regen_sop_60s, + regen_sop_continuous, + charge_sop, + ac_charge_sop, + create_time, + update_time, + create_by, + update_by, + + + #{deviceId}, + #{timestamp}, + #{dischargeSop10s}, + #{dischargeSop30s}, + #{dischargeSop60s}, + #{dischargeSopContinuous}, + #{regenSop10s}, + #{regenSop30s}, + #{regenSop60s}, + #{regenSopContinuous}, + #{chargeSop}, + #{acChargeSop}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + + + diff --git a/src/main/resources/mybatis/bms/BmsSystemStatusMapper.xml b/src/main/resources/mybatis/bms/BmsSystemStatusMapper.xml new file mode 100644 index 0000000..7f92049 --- /dev/null +++ b/src/main/resources/mybatis/bms/BmsSystemStatusMapper.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + insert into bms_system_status + + device_id, + timestamp, + relay_status, + onaacc_status, + reset_count, + cycle_count, + sample_25v, + sample_5v, + cell_count, + temp_sensor_count, + create_time, + update_time, + create_by, + update_by, + + + #{deviceId}, + #{timestamp}, + #{relayStatus}, + #{onaaccStatus}, + #{resetCount}, + #{cycleCount}, + #{sample25v}, + #{sample5v}, + #{cellCount}, + #{tempSensorCount}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + + + diff --git a/src/main/resources/ota/0.0.1.bin b/src/main/resources/ota/0.0.6.bin similarity index 68% rename from src/main/resources/ota/0.0.1.bin rename to src/main/resources/ota/0.0.6.bin index ab25ad75d2b6cddd038c076afbf4b9e131a5c1b5..1f674de3038a51241618f4f91d443b8aef3d0ccf 100644 GIT binary patch delta 18319 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 diff --git a/src/main/resources/ota/1.0.2.bin b/src/main/resources/ota/1.0.2.bin deleted file mode 100644 index c598fec..0000000 --- a/src/main/resources/ota/1.0.2.bin +++ /dev/null @@ -1 +0,0 @@ -qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶qweqweqweqwzxcasdasdasdasdasd按时打算大叔大婶 \ No newline at end of file diff --git a/src/main/resources/ota/2.2.1.bin b/src/main/resources/ota/2.2.1.bin deleted file mode 100644 index ff899bc345158c89f3af960a1950f53400635a3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124756 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 hmX*!|^-L{{v3u=t}?q