Compare commits
237 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 056e06b00a | |||
| 515f440298 | |||
| e4cfd15cb4 | |||
| 5ab2cb8f90 | |||
| 4e7d387edf | |||
| 49ed5f218a | |||
| 8806473080 | |||
| 71d0b0f609 | |||
| 6253fb6b2d | |||
| 21673ecd1e | |||
| 66de6fe77c | |||
| 63ed2641ee | |||
| 5d5a7137fc | |||
| 6545b4f947 | |||
| 8a44009c42 | |||
| 2b22b70baa | |||
| 5a86769b40 | |||
| eeccd19f0a | |||
| d19f07c4e8 | |||
| 9f2c303047 | |||
| 7199677d7b | |||
| 4b58838d5d | |||
| 14904ea6b2 | |||
| 5225f03195 | |||
| 619c05e965 | |||
| 1531af2969 | |||
| e55cdc6504 | |||
| 400ae98b4e | |||
| dd060b0abf | |||
| 6ae9126662 | |||
| 83fae710fb | |||
| c518b9bcae | |||
| 034c682864 | |||
| 5f7604d36b | |||
| 084201bb58 | |||
| 194429af2d | |||
| b3f7ca7a81 | |||
| 5eccb35568 | |||
| c007016224 | |||
| 768dd0bead | |||
| 26c534cae9 | |||
| 24ba30a536 | |||
| bb85ca1d5e | |||
| ff76a73a61 | |||
| 1db167f0f8 | |||
| f592b709a7 | |||
| 7f025be41b | |||
| 8968dc7a41 | |||
| 46ea4acc2c | |||
| 04bf9fe1c1 | |||
| b4f867f796 | |||
| ecc4799afd | |||
| 3287cddc6a | |||
| 11e217787b | |||
| fec45dac03 | |||
| 939bcbe950 | |||
| 12a459854e | |||
| a31a1a1caa | |||
| 8716d43879 | |||
| dd0132ab2f | |||
| fb64be5a5a | |||
| 0076872134 | |||
| f05ba30f14 | |||
| 9aa7dd9d18 | |||
| 3f4d3772b0 | |||
| 685a2e8687 | |||
| ebc06e2a2d | |||
| 2fe5d7b8a1 | |||
| 31fe793e97 | |||
| 1eee07e040 | |||
| 566119f692 | |||
| 77a3b6e855 | |||
| a4ac11e7d9 | |||
| 4f3cd8c82b | |||
| 873119a1ff | |||
| 6ed7a25819 | |||
| dbdf1a5a61 | |||
| 2278b63ada | |||
| b032e8e073 | |||
| f24072f248 | |||
| 041c41822e | |||
| 552d471fde | |||
| e381ff86c8 | |||
| 2c9cc1f1d4 | |||
| 93ed334c0e | |||
| bac66aaddb | |||
| 5456d4742e | |||
| 22415df926 | |||
| 9e086b77b5 | |||
| 56613e8f70 | |||
| 18ff0100b9 | |||
| 90e9b004b4 | |||
| 4f43b043ac | |||
| eb6f44dd93 | |||
| d0bab53dee | |||
| 289fbc4d3a | |||
| 21fe3af17c | |||
| 944adf3503 | |||
| 15a25b5ada | |||
| 289fd37e2b | |||
| 2cd4040431 | |||
| 245b3e22d6 | |||
| ff487f1b05 | |||
| 16d414f092 | |||
| 41b846c7f9 | |||
| 6af6232b51 | |||
| 40e23df66a | |||
| 3611e444da | |||
| 7b25674a25 | |||
| d387612b07 | |||
| 85b370ea4c | |||
| c6fca8b939 | |||
| e73787ee8f | |||
| 0e1c7f52f1 | |||
| 73c668709a | |||
| 3488808786 | |||
| 497e5bb8ca | |||
| 678e814350 | |||
| 5f1e621da2 | |||
| b7aaf85a3f | |||
| 28dfb0c152 | |||
| fc5f56b3b0 | |||
| 3313c7f9cd | |||
| 70c990051b | |||
| b4b78e03b1 | |||
| 3605702209 | |||
| 62340387bb | |||
| a31b607872 | |||
| 8e631b7b30 | |||
| 3735c4f4d8 | |||
| c068e7d4ab | |||
| b38bc36ad6 | |||
| d9c0ff733a | |||
| e87abc2807 | |||
| a5e977c0d1 | |||
| 7298acc785 | |||
| 55d648d12c | |||
| 36f08e5f06 | |||
| 1cc7d53559 | |||
| 3753075b10 | |||
| aab2b1d94e | |||
| 3ee99fa8d4 | |||
| c62aa46fcb | |||
| bfbb0ae475 | |||
| 25e03ab028 | |||
| 38ade0c2ed | |||
| f1e819ba2b | |||
| be4da8c58d | |||
| fa280097eb | |||
| 1b5fcea5cd | |||
| 49a83fd420 | |||
| 5381cd597c | |||
| 43edc47aaa | |||
| 72c7c0c3e0 | |||
| c2682f38a8 | |||
| 3598cb2d66 | |||
| 82e63e28d3 | |||
| 614cca4297 | |||
| d83af112e7 | |||
| 75bd5f6bf2 | |||
| 0108b4f108 | |||
| 4a0075b606 | |||
| b776f6ff76 | |||
| 76b30c715c | |||
| e1fb6e30ac | |||
| a338c921d3 | |||
| 4ad2cafc5d | |||
| d0275e62e0 | |||
| 94e18c029c | |||
| 391ed354f8 | |||
| 6716852435 | |||
| 451b2f6766 | |||
| 250318ddef | |||
| d468ef9941 | |||
| 15b964b4ce | |||
| 3020f9f915 | |||
| 609803605d | |||
| aadf66d5ad | |||
| 049eaa84b7 | |||
| c438f50ae1 | |||
| eed6f839f6 | |||
| 6ebeb48b62 | |||
| 3454359f01 | |||
| 83f8f4e293 | |||
| b79f9caa2d | |||
| e33b26fc05 | |||
| 9075878e41 | |||
| 158bc1a51d | |||
| b861ad7593 | |||
| 636a2ab73b | |||
| d8d0a83c87 | |||
| 7121fdecfa | |||
| 2bbc0abc08 | |||
| e5a2ce4c8d | |||
| f2b2741b1c | |||
| 097709d403 | |||
| 33534cc500 | |||
| ee53ab137b | |||
| b2d5023122 | |||
| 553e30c6ef | |||
| 102ecbd41c | |||
| d47d9150e6 | |||
| 46135aa2ec | |||
| 9874d6b1ea | |||
| f7515b221b | |||
| f3b34e7fb5 | |||
| 2810f5e204 | |||
| 7cd5a9b131 | |||
| 2099b94e73 | |||
| 630f77a783 | |||
| 8e8c57cb64 | |||
| a5f1444984 | |||
| e93f9cc6b8 | |||
| 8954329ccc | |||
| b8d3643ba9 | |||
| 2451e00022 | |||
| cb39131871 | |||
| 2bb78dc020 | |||
| d807b9117d | |||
| 8a4cff0a6c | |||
| ecbe55cdbf | |||
| bfdbc4f42c | |||
| ab6771d33d | |||
| 2344cc458c | |||
| aa37e2a881 | |||
| 15e230babb | |||
| 7374d7708b | |||
| 26bbe6deee | |||
| 1a1558f8cd | |||
| d6276dc21f | |||
| 725ae14548 | |||
| b2527507e4 | |||
| ff058dceaf | |||
| 76d0634ae8 | |||
| eb72542b67 | |||
| 7927066058 | |||
| 08c83c1a37 |
@ -39,8 +39,9 @@
|
||||
|
||||
<!-- Mysql驱动包 -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>8.0.33</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 核心模块-->
|
||||
|
||||
@ -55,4 +55,21 @@ public class EmsAlarmRecordsController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭告警
|
||||
*/
|
||||
@PostMapping("/closeAlarm")
|
||||
public AjaxResult closeAlarm(@RequestBody EmsAlarmRecords emsAlarmRecords)
|
||||
{
|
||||
Long id = emsAlarmRecords.getId();
|
||||
if (id == null) {
|
||||
return error("告警id不能为空");
|
||||
}
|
||||
String result = iEmsAlarmRecordsService.closeAlarm(id, getUserId());
|
||||
if ("success".equals(result) || "告警已关闭".equals(result)) {
|
||||
return AjaxResult.success("操作成功");
|
||||
}
|
||||
return error(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.ems.domain.EmsDailyChargeData;
|
||||
import com.xzzn.ems.service.IEmsDailyChargeDataService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 站点管理-充放电修正 Controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ems/dailyChargeData")
|
||||
public class EmsDailyChargeDataController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IEmsDailyChargeDataService emsDailyChargeDataService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(EmsDailyChargeData emsDailyChargeData) {
|
||||
startPage();
|
||||
List<EmsDailyChargeData> list = emsDailyChargeDataService.selectEmsDailyChargeDataList(emsDailyChargeData);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id) {
|
||||
return success(emsDailyChargeDataService.selectEmsDailyChargeDataById(id));
|
||||
}
|
||||
|
||||
@Log(title = "站点管理-充放电修正", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody EmsDailyChargeData emsDailyChargeData) {
|
||||
return toAjax(emsDailyChargeDataService.insertEmsDailyChargeData(emsDailyChargeData, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "站点管理-充放电修正", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody EmsDailyChargeData emsDailyChargeData) {
|
||||
return toAjax(emsDailyChargeDataService.updateEmsDailyChargeData(emsDailyChargeData, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "站点管理-充放电修正", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids) {
|
||||
return toAjax(emsDailyChargeDataService.deleteEmsDailyChargeDataByIds(ids));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.ems.domain.EmsDailyEnergyData;
|
||||
import com.xzzn.ems.service.IEmsDailyEnergyDataService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 站点管理-数据修正 Controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ems/dailyEnergyData")
|
||||
public class EmsDailyEnergyDataController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IEmsDailyEnergyDataService emsDailyEnergyDataService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(EmsDailyEnergyData emsDailyEnergyData) {
|
||||
startPage();
|
||||
List<EmsDailyEnergyData> list = emsDailyEnergyDataService.selectEmsDailyEnergyDataList(emsDailyEnergyData);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id) {
|
||||
return success(emsDailyEnergyDataService.selectEmsDailyEnergyDataById(id));
|
||||
}
|
||||
|
||||
@Log(title = "站点管理-数据修正", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody EmsDailyEnergyData emsDailyEnergyData) {
|
||||
return toAjax(emsDailyEnergyDataService.insertEmsDailyEnergyData(emsDailyEnergyData, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "站点管理-数据修正", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody EmsDailyEnergyData emsDailyEnergyData) {
|
||||
return toAjax(emsDailyEnergyDataService.updateEmsDailyEnergyData(emsDailyEnergyData, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "站点管理-数据修正", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids) {
|
||||
return toAjax(emsDailyEnergyDataService.deleteEmsDailyEnergyDataByIds(ids));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.xzzn.ems.domain.vo.EnergyPriceVo;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.ems.service.IEmsEnergyPriceConfigService;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 电价配置Controller
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-09-28
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ems/energyPriceConfig")
|
||||
public class EmsEnergyPriceConfigController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IEmsEnergyPriceConfigService emsEnergyPriceConfigService;
|
||||
|
||||
/**
|
||||
* 查询电价配置列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(String siteId, String startTime,String endTime)
|
||||
{
|
||||
List<EnergyPriceVo> list = emsEnergyPriceConfigService.selectEmsEnergyPriceConfigList(siteId,startTime,endTime);
|
||||
return getDataTable2(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取电价配置详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:query')")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
return success(emsEnergyPriceConfigService.selectEmsEnergyPriceConfigById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增电价配置
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:add')")
|
||||
@Log(title = "电价配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody EnergyPriceVo priceVo)
|
||||
{
|
||||
return toAjax(emsEnergyPriceConfigService.insertEmsEnergyPriceConfig(priceVo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改电价配置
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:edit')")
|
||||
@Log(title = "电价配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody EnergyPriceVo priceVo)
|
||||
{
|
||||
return toAjax(emsEnergyPriceConfigService.updateEmsEnergyPriceConfig(priceVo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除电价配置
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:remove')")
|
||||
@Log(title = "电价配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids)
|
||||
{
|
||||
return toAjax(emsEnergyPriceConfigService.deleteEmsEnergyPriceConfigByIds(ids));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
|
||||
import com.xzzn.common.utils.poi.ExcelUtil;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 故障告警保护方案Controller
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-10-22
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ems/protectPlan")
|
||||
public class EmsFaultProtectionPlanController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IEmsFaultProtectionPlanService emsFaultProtectionPlanService;
|
||||
|
||||
/**
|
||||
* 查询故障告警保护方案列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:plan:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(EmsFaultProtectionPlan emsFaultProtectionPlan)
|
||||
{
|
||||
startPage();
|
||||
List<EmsFaultProtectionPlan> list = emsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(emsFaultProtectionPlan);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出故障告警保护方案列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:plan:export')")
|
||||
@Log(title = "故障告警保护方案", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, EmsFaultProtectionPlan emsFaultProtectionPlan)
|
||||
{
|
||||
List<EmsFaultProtectionPlan> list = emsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(emsFaultProtectionPlan);
|
||||
ExcelUtil<EmsFaultProtectionPlan> util = new ExcelUtil<EmsFaultProtectionPlan>(EmsFaultProtectionPlan.class);
|
||||
util.exportExcel(response, list, "故障告警保护方案数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取故障告警保护方案详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:plan:query')")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
return success(emsFaultProtectionPlanService.selectEmsFaultProtectionPlanById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增故障告警保护方案
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:plan:add')")
|
||||
@Log(title = "故障告警保护方案", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody EmsFaultProtectionPlan emsFaultProtectionPlan)
|
||||
{
|
||||
emsFaultProtectionPlan.setCreateBy(getUsername());
|
||||
return toAjax(emsFaultProtectionPlanService.insertEmsFaultProtectionPlan(emsFaultProtectionPlan));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改故障告警保护方案
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:plan:edit')")
|
||||
@Log(title = "故障告警保护方案", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody EmsFaultProtectionPlan emsFaultProtectionPlan)
|
||||
{
|
||||
emsFaultProtectionPlan.setUpdateBy(getUsername());
|
||||
return toAjax(emsFaultProtectionPlanService.updateEmsFaultProtectionPlan(emsFaultProtectionPlan));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除故障告警保护方案
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:plan:remove')")
|
||||
@Log(title = "故障告警保护方案", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids)
|
||||
{
|
||||
return toAjax(emsFaultProtectionPlanService.deleteEmsFaultProtectionPlanByIds(ids));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.enums.DeviceCategory;
|
||||
import com.xzzn.ems.domain.vo.*;
|
||||
import com.xzzn.ems.service.IGeneralQueryService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* 综合查询
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ems/generalQuery")
|
||||
public class EmsGeneralQueryController extends BaseController{
|
||||
|
||||
@Autowired
|
||||
private IGeneralQueryService iGeneralQueryService;
|
||||
|
||||
/**
|
||||
* 获取设备枚举
|
||||
*/
|
||||
@GetMapping("/getAllDeviceCategory")
|
||||
public AjaxResult getDeviceCategory()
|
||||
{
|
||||
// 获取所有枚举的信息
|
||||
List<Map<String, String>> deviceCategoryList = new ArrayList<>();
|
||||
for (DeviceCategory category : DeviceCategory.values()) {
|
||||
Map<String, String> categoryMap = new HashMap<>();
|
||||
categoryMap.put("name", category.getInfo());
|
||||
categoryMap.put("code", category.getCode());
|
||||
deviceCategoryList.add(categoryMap);
|
||||
}
|
||||
return success(deviceCategoryList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 点位模糊查询
|
||||
*/
|
||||
@PostMapping("/pointFuzzyQuery")
|
||||
public AjaxResult pointFuzzyQuery(@RequestBody PointNameRequest request)
|
||||
{
|
||||
return success(iGeneralQueryService.getPointNameList(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据点位查询点位数据变化
|
||||
*/
|
||||
@PostMapping("/getPointValueList")
|
||||
public AjaxResult getPointValueList(@RequestBody PointNameRequest request)
|
||||
{
|
||||
List<GeneralQueryResponse> result = new ArrayList<>();
|
||||
try {
|
||||
result = iGeneralQueryService.getPointValueList(request);
|
||||
} catch (Exception e) {
|
||||
logger.error("<UNK>",e);
|
||||
return error("报错请重试!");
|
||||
}
|
||||
return success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备枚举
|
||||
*/
|
||||
@GetMapping("/getAllBatteryIdsBySites/{siteIds}")
|
||||
public AjaxResult getAllBatteryIdsBySites(@PathVariable String[] siteIds)
|
||||
{
|
||||
return success(iGeneralQueryService.getAllBatteryIdsBySites(siteIds));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.ems.domain.EmsMqttTopicConfig;
|
||||
import com.xzzn.ems.service.IEmsMqttTopicConfigService;
|
||||
import com.xzzn.common.utils.poi.ExcelUtil;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
* 站点topic配置Controller
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-11-06
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ems/mqttConfig")
|
||||
public class EmsMqttTopicConfigController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IEmsMqttTopicConfigService emsMqttTopicConfigService;
|
||||
|
||||
/**
|
||||
* 查询站点topic配置列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(EmsMqttTopicConfig emsMqttTopicConfig)
|
||||
{
|
||||
startPage();
|
||||
List<EmsMqttTopicConfig> list = emsMqttTopicConfigService.selectEmsMqttTopicConfigList(emsMqttTopicConfig);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出站点topic配置列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:export')")
|
||||
@Log(title = "站点topic配置", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, EmsMqttTopicConfig emsMqttTopicConfig)
|
||||
{
|
||||
List<EmsMqttTopicConfig> list = emsMqttTopicConfigService.selectEmsMqttTopicConfigList(emsMqttTopicConfig);
|
||||
ExcelUtil<EmsMqttTopicConfig> util = new ExcelUtil<EmsMqttTopicConfig>(EmsMqttTopicConfig.class);
|
||||
util.exportExcel(response, list, "站点topic配置数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取站点topic配置详细信息
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:query')")
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
return success(emsMqttTopicConfigService.selectEmsMqttTopicConfigById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增站点topic配置
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:add')")
|
||||
@Log(title = "站点topic配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody EmsMqttTopicConfig emsMqttTopicConfig)
|
||||
{
|
||||
emsMqttTopicConfig.setCreateBy(getUsername());
|
||||
return toAjax(emsMqttTopicConfigService.insertEmsMqttTopicConfig(emsMqttTopicConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改站点topic配置
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:edit')")
|
||||
@Log(title = "站点topic配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody EmsMqttTopicConfig emsMqttTopicConfig)
|
||||
{
|
||||
emsMqttTopicConfig.setUpdateBy(getUsername());
|
||||
return toAjax(emsMqttTopicConfigService.updateEmsMqttTopicConfig(emsMqttTopicConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除站点topic配置
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:config:remove')")
|
||||
@Log(title = "站点topic配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids)
|
||||
{
|
||||
return toAjax(emsMqttTopicConfigService.deleteEmsMqttTopicConfigByIds(ids));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.ems.domain.EmsPointCalcConfig;
|
||||
import com.xzzn.ems.service.IEmsPointCalcConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/ems/pointCalcConfig")
|
||||
public class EmsPointCalcConfigController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IEmsPointCalcConfigService pointCalcConfigService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(EmsPointCalcConfig pointCalcConfig) {
|
||||
startPage();
|
||||
List<EmsPointCalcConfig> list = pointCalcConfigService.selectPointCalcConfigList(pointCalcConfig);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id) {
|
||||
return success(pointCalcConfigService.selectPointCalcConfigById(id));
|
||||
}
|
||||
|
||||
@Log(title = "计算点配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody EmsPointCalcConfig pointCalcConfig) {
|
||||
return toAjax(pointCalcConfigService.insertPointCalcConfig(pointCalcConfig, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "计算点配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody EmsPointCalcConfig pointCalcConfig) {
|
||||
return toAjax(pointCalcConfigService.updatePointCalcConfig(pointCalcConfig, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "计算点配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids) {
|
||||
return toAjax(pointCalcConfigService.deletePointCalcConfigByIds(ids));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.ems.domain.EmsPointConfig;
|
||||
import com.xzzn.ems.domain.vo.ImportPointTemplateRequest;
|
||||
import com.xzzn.ems.domain.vo.PointConfigCurveRequest;
|
||||
import com.xzzn.ems.domain.vo.PointConfigGenerateRecentRequest;
|
||||
import com.xzzn.ems.domain.vo.PointConfigLatestValueRequest;
|
||||
import com.xzzn.ems.service.IEmsPointConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/ems/pointConfig")
|
||||
public class EmsPointConfigController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IEmsPointConfigService pointConfigService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(EmsPointConfig pointConfig) {
|
||||
startPage();
|
||||
List<EmsPointConfig> list = pointConfigService.selectPointConfigList(pointConfig);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id) {
|
||||
return success(pointConfigService.selectPointConfigById(id));
|
||||
}
|
||||
|
||||
@Log(title = "点位配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody EmsPointConfig pointConfig) {
|
||||
return toAjax(pointConfigService.insertPointConfig(pointConfig, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "点位配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody EmsPointConfig pointConfig) {
|
||||
return toAjax(pointConfigService.updatePointConfig(pointConfig, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "点位配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids) {
|
||||
return toAjax(pointConfigService.deletePointConfigByIds(ids));
|
||||
}
|
||||
|
||||
@Log(title = "点位配置", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importTemplateBySite")
|
||||
public AjaxResult importTemplateBySite(@Valid @RequestBody ImportPointTemplateRequest request) {
|
||||
return success(pointConfigService.importTemplateBySite(request, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "点位配置", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importCsv")
|
||||
public AjaxResult importCsv(@RequestParam String siteId,
|
||||
@RequestParam(required = false) Boolean overwrite,
|
||||
@RequestParam("file") MultipartFile file) {
|
||||
return success(pointConfigService.importCsvBySite(siteId, overwrite, file, getUsername()));
|
||||
}
|
||||
|
||||
@GetMapping("/registerAddress")
|
||||
public AjaxResult getRegisterAddress(@RequestParam String siteId,
|
||||
@RequestParam String deviceCategory,
|
||||
@RequestParam String deviceId,
|
||||
@RequestParam String dataKey) {
|
||||
return success(pointConfigService.getRegisterAddress(siteId, deviceCategory, deviceId, dataKey));
|
||||
}
|
||||
|
||||
@PostMapping("/latestValues")
|
||||
public AjaxResult latestValues(@RequestBody PointConfigLatestValueRequest request) {
|
||||
return success(pointConfigService.getLatestValues(request));
|
||||
}
|
||||
|
||||
@PostMapping("/curve")
|
||||
public AjaxResult curve(@RequestBody PointConfigCurveRequest request) {
|
||||
return success(pointConfigService.getCurveData(request));
|
||||
}
|
||||
|
||||
@Log(title = "点位配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/generateRecent7Days")
|
||||
public AjaxResult generateRecent7Days(@Valid @RequestBody PointConfigGenerateRecentRequest request) {
|
||||
return success(pointConfigService.generateRecent7DaysData(request));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,159 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.ems.domain.EmsPointMatch;
|
||||
import com.xzzn.ems.domain.vo.DevicePointMatchExportVo;
|
||||
import com.xzzn.ems.domain.vo.DevicePointMatchVo;
|
||||
import com.xzzn.ems.domain.vo.ImportPointDataRequest;
|
||||
import com.xzzn.ems.domain.vo.ImportPointTemplateRequest;
|
||||
import com.xzzn.ems.service.IEmsPointMatchService;
|
||||
import com.xzzn.common.utils.poi.ExcelUtil;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 点位匹配Controller
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-11-04
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ems/pointMatch")
|
||||
public class EmsPointMatchController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private IEmsPointMatchService emsPointMatchService;
|
||||
|
||||
/**
|
||||
* 导出点位匹配列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:match:export')")
|
||||
@Log(title = "点位匹配", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, EmsPointMatch emsPointMatch)
|
||||
{
|
||||
List<DevicePointMatchExportVo> list = emsPointMatchService.selectEmsPointMatchList(emsPointMatch);
|
||||
ExcelUtil<DevicePointMatchExportVo> util = new ExcelUtil<>(DevicePointMatchExportVo.class);
|
||||
util.exportExcel(response, list, "点位匹配数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询点位配置列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public com.xzzn.common.core.page.TableDataInfo list(EmsPointMatch emsPointMatch)
|
||||
{
|
||||
startPage();
|
||||
List<EmsPointMatch> list = emsPointMatchService.selectPointMatchConfigList(emsPointMatch);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询点位配置详情
|
||||
*/
|
||||
@GetMapping(value = "/{id}")
|
||||
public AjaxResult getInfo(@PathVariable("id") Long id)
|
||||
{
|
||||
return success(emsPointMatchService.selectPointMatchById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增点位配置
|
||||
*/
|
||||
@Log(title = "点位配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody EmsPointMatch emsPointMatch)
|
||||
{
|
||||
emsPointMatch.setCreateBy(getUsername());
|
||||
return toAjax(emsPointMatchService.insertPointMatch(emsPointMatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改点位配置
|
||||
*/
|
||||
@Log(title = "点位配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody EmsPointMatch emsPointMatch)
|
||||
{
|
||||
emsPointMatch.setUpdateBy(getUsername());
|
||||
return toAjax(emsPointMatchService.updatePointMatch(emsPointMatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除点位配置
|
||||
*/
|
||||
@Log(title = "点位配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids)
|
||||
{
|
||||
return toAjax(emsPointMatchService.deletePointMatchByIds(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传点位清单
|
||||
* @param file
|
||||
* @param updateSupport
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:user:import')")
|
||||
@Log(title = "点位匹配", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importData")
|
||||
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
|
||||
{
|
||||
ExcelUtil<EmsPointMatch> util = new ExcelUtil<EmsPointMatch>(EmsPointMatch.class);
|
||||
List<EmsPointMatch> pointMatcheList = util.importExcel(file.getInputStream());
|
||||
String operName = getUsername();
|
||||
String message = emsPointMatchService.importPoint(pointMatcheList, updateSupport, operName);
|
||||
return success(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备的点位清单
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:user:import')")
|
||||
@Log(title = "点位匹配", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importDataByDevice")
|
||||
public void importDataByDevice(@Valid ImportPointDataRequest request, HttpServletResponse response) {
|
||||
List<DevicePointMatchVo> list = emsPointMatchService.importDataByDevice(request, getUsername());
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
ExcelUtil<DevicePointMatchVo> util = new ExcelUtil<>(DevicePointMatchVo.class);
|
||||
util.exportExcel(response, list, "点位匹配数据");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据站点导入模板点位配置
|
||||
* @param request 请求参数
|
||||
* @return 导入结果
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:user:import')")
|
||||
@Log(title = "点位配置", businessType = BusinessType.IMPORT)
|
||||
@PostMapping("/importTemplateBySite")
|
||||
public AjaxResult importTemplateBySite(@Valid @RequestBody ImportPointTemplateRequest request)
|
||||
{
|
||||
String message = emsPointMatchService.importTemplateBySite(request, getUsername());
|
||||
return success(message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,22 +1,40 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.config.RuoYiConfig;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.common.utils.file.FileUploadUtils;
|
||||
import com.xzzn.common.utils.file.MimeTypeUtils;
|
||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
||||
import com.xzzn.ems.domain.EmsSiteSetting;
|
||||
import com.xzzn.ems.domain.vo.DeviceUpdateRequest;
|
||||
import com.xzzn.ems.domain.vo.DevicesSettingVo;
|
||||
import com.xzzn.ems.domain.vo.PointDataRequest;
|
||||
import com.xzzn.ems.domain.vo.PointQueryResponse;
|
||||
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
|
||||
import com.xzzn.ems.domain.vo.SiteDeviceListVo;
|
||||
import com.xzzn.ems.domain.vo.WorkStatusEnumMappingSaveRequest;
|
||||
import com.xzzn.ems.service.IEmsDeviceSettingService;
|
||||
import com.xzzn.ems.service.IEmsSiteService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
*
|
||||
* 站点配置
|
||||
@ -43,14 +61,36 @@ public class EmsSiteConfigController extends BaseController{
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增站点
|
||||
*/
|
||||
@PostMapping("/addSite")
|
||||
public AjaxResult addSite(@RequestBody EmsSiteSetting emsSiteSetting)
|
||||
{
|
||||
emsSiteSetting.setCreateBy(getUsername());
|
||||
return toAjax(iEmsSiteService.addSite(emsSiteSetting));
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑站点
|
||||
*/
|
||||
@PostMapping("/updateSite")
|
||||
public AjaxResult updateSite(@RequestBody EmsSiteSetting emsSiteSetting)
|
||||
{
|
||||
emsSiteSetting.setUpdateBy(getUsername());
|
||||
return toAjax(iEmsSiteService.updateSite(emsSiteSetting));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备列表-分页
|
||||
*/
|
||||
@GetMapping("/getDeviceInfoList")
|
||||
public TableDataInfo getDeviceInfoList(@RequestParam String siteId)
|
||||
public TableDataInfo getDeviceInfoList(@RequestParam(value = "siteId", required = false) String siteId,
|
||||
@RequestParam(value = "deviceCategory", required = false) String deviceCategory)
|
||||
{
|
||||
startPage();
|
||||
List<SiteDeviceListVo> list = iEmsSiteService.getAllDeviceList(siteId);
|
||||
List<SiteDeviceListVo> list = iEmsSiteService.getAllDeviceListNoDisp(siteId, deviceCategory);
|
||||
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@ -69,7 +109,7 @@ public class EmsSiteConfigController extends BaseController{
|
||||
@GetMapping("/getDeviceList")
|
||||
public AjaxResult getDeviceInfoList2(@RequestParam String siteId)
|
||||
{
|
||||
return success(iEmsSiteService.getAllDeviceList(siteId));
|
||||
return success(iEmsSiteService.getAllDeviceList(siteId, null));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,7 +125,7 @@ public class EmsSiteConfigController extends BaseController{
|
||||
* 新增设备
|
||||
*/
|
||||
@PostMapping("/addDevice")
|
||||
public AjaxResult addDevice(@RequestBody EmsDevicesSetting devicesSetting)
|
||||
public AjaxResult addDevice(@RequestBody DevicesSettingVo devicesSetting)
|
||||
{
|
||||
int result = iEmsDeviceSettingService.addDevice(devicesSetting);
|
||||
if (result > 0) {
|
||||
@ -115,7 +155,7 @@ public class EmsSiteConfigController extends BaseController{
|
||||
* 修改Modbus设备配置
|
||||
*/
|
||||
@PostMapping("/updateDevice")
|
||||
public AjaxResult updateDevice(@RequestBody EmsDevicesSetting emsDevicesSetting)
|
||||
public AjaxResult updateDevice(@RequestBody DevicesSettingVo emsDevicesSetting)
|
||||
{
|
||||
int result = iEmsDeviceSettingService.updateDevice(emsDevicesSetting);
|
||||
if (result > 0) {
|
||||
@ -136,4 +176,90 @@ public class EmsSiteConfigController extends BaseController{
|
||||
{
|
||||
return toAjax(iEmsDeviceSettingService.deleteEmsDevicesSettingById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个站点单个设备点位查询-点位清单
|
||||
*/
|
||||
@GetMapping("/getDevicePointList")
|
||||
public TableDataInfo getDevicePointList(@Validated PointDataRequest request)
|
||||
{
|
||||
List<PointQueryResponse> result = iEmsDeviceSettingService.getSingleSiteDevicePoints(request);
|
||||
return getDataTable2(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定站点下的所有设备类别
|
||||
*/
|
||||
@GetMapping("/getSiteAllDeviceCategory")
|
||||
public AjaxResult getSiteAllDeviceCategory(String siteId)
|
||||
{
|
||||
return success(iEmsDeviceSettingService.getSiteAllDeviceCategory(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备类别获取父类的设备id
|
||||
*/
|
||||
@GetMapping("/getParentDeviceId")
|
||||
public AjaxResult getParentDeviceId(@RequestParam String siteId, @RequestParam String deviceCategory)
|
||||
{
|
||||
return success(iEmsSiteService.getParentCategoryDeviceId(siteId, deviceCategory));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定站点下的指定设备类型的设备
|
||||
*/
|
||||
@GetMapping("/getDeviceListBySiteAndCategory")
|
||||
public AjaxResult getDeviceListBySiteAndCategory(String siteId,String deviceCategory)
|
||||
{
|
||||
return success(iEmsDeviceSettingService.getDeviceListBySiteAndCategory(siteId, deviceCategory));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单站监控项目点位映射
|
||||
*/
|
||||
@GetMapping("/getSingleMonitorProjectPointMapping")
|
||||
public AjaxResult getSingleMonitorProjectPointMapping(@RequestParam String siteId)
|
||||
{
|
||||
return success(iEmsDeviceSettingService.getSiteMonitorProjectPointMapping(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存单站监控项目点位映射
|
||||
*/
|
||||
@PostMapping("/saveSingleMonitorProjectPointMapping")
|
||||
public AjaxResult saveSingleMonitorProjectPointMapping(@RequestBody SiteMonitorProjectPointMappingSaveRequest request)
|
||||
{
|
||||
int rows = iEmsDeviceSettingService.saveSiteMonitorProjectPointMapping(request, getUsername());
|
||||
return AjaxResult.success(rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单站监控工作状态枚举映射(PCS)
|
||||
*/
|
||||
@GetMapping("/getSingleMonitorWorkStatusEnumMappings")
|
||||
public AjaxResult getSingleMonitorWorkStatusEnumMappings(@RequestParam String siteId)
|
||||
{
|
||||
return success(iEmsDeviceSettingService.getSiteWorkStatusEnumMappings(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存单站监控工作状态枚举映射(PCS)
|
||||
*/
|
||||
@PostMapping("/saveSingleMonitorWorkStatusEnumMappings")
|
||||
public AjaxResult saveSingleMonitorWorkStatusEnumMappings(@RequestBody WorkStatusEnumMappingSaveRequest request)
|
||||
{
|
||||
int rows = iEmsDeviceSettingService.saveSiteWorkStatusEnumMappings(request.getSiteId(), request.getMappings(), getUsername());
|
||||
return AjaxResult.success(rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* PCS设备开关机
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('system:device:onAndOff')")
|
||||
@Log(title = "开关机", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/updateDeviceStatus")
|
||||
public AjaxResult updateDeviceStatus(@Valid @RequestBody DeviceUpdateRequest request)
|
||||
{
|
||||
return success(iEmsDeviceSettingService.updateDeviceStatus(request));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.ems.service.IEmsSiteService;
|
||||
import com.xzzn.ems.domain.vo.DateSearchRequest;
|
||||
import com.xzzn.ems.service.IHomePageService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
*
|
||||
// * 站点地图
|
||||
* 站点地图
|
||||
*
|
||||
*/
|
||||
@RestController
|
||||
@ -31,4 +31,17 @@ public class EmsSiteMapController extends BaseController{
|
||||
return success(homePageService.getSingleSiteBaseInfo(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个站点7天充放电数据
|
||||
*/
|
||||
@GetMapping("/getSevenChargeData")
|
||||
public AjaxResult getSevenChargeData(DateSearchRequest request)
|
||||
{
|
||||
String siteId = request.getSiteId();
|
||||
if(siteId == null || siteId.isEmpty()) {
|
||||
return error("站点必传");
|
||||
}
|
||||
return success(homePageService.getSevenChargeData(request));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,14 +3,24 @@ package com.xzzn.web.controller.ems;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.vo.BMSBatteryDataList;
|
||||
import com.xzzn.ems.domain.vo.BatteryDataStatsListVo;
|
||||
import com.xzzn.ems.domain.vo.DateSearchRequest;
|
||||
import com.xzzn.ems.domain.vo.RunningGraphRequest;
|
||||
import com.xzzn.ems.domain.vo.SiteBatteryDataList;
|
||||
import com.xzzn.ems.domain.vo.SiteMonitorDataSaveRequest;
|
||||
import com.xzzn.ems.domain.vo.SiteMonitorRuningInfoVo;
|
||||
import com.xzzn.ems.service.IEmsDeviceSettingService;
|
||||
import com.xzzn.ems.service.IEmsSiteService;
|
||||
import com.xzzn.ems.service.IEmsStatsReportService;
|
||||
import com.xzzn.ems.service.ISingleSiteService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -21,6 +31,8 @@ import java.util.List;
|
||||
@RestController
|
||||
@RequestMapping("/ems/siteMonitor")
|
||||
public class EmsSiteMonitorController extends BaseController{
|
||||
private static final Logger log = LoggerFactory.getLogger(EmsSiteMonitorController.class);
|
||||
private static final String RUNNING_GRAPH_CTRL_DEBUG = "RunningGraphCtrlDebug";
|
||||
|
||||
@Autowired
|
||||
private ISingleSiteService iSingleSiteService;
|
||||
@ -28,6 +40,8 @@ public class EmsSiteMonitorController extends BaseController{
|
||||
private IEmsSiteService iEmsSiteService;
|
||||
@Autowired
|
||||
private IEmsStatsReportService iemsStatsReportService;
|
||||
@Autowired
|
||||
private IEmsDeviceSettingService iEmsDeviceSettingService;
|
||||
|
||||
/**
|
||||
* 获取单站首页数据
|
||||
@ -38,6 +52,15 @@ public class EmsSiteMonitorController extends BaseController{
|
||||
return success(iSingleSiteService.getSiteMonitorDataVo(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单站首页总累计运行数据(基于日表)
|
||||
*/
|
||||
@GetMapping("/homeTotalView")
|
||||
public AjaxResult getSingleSiteHomeTotalView(@RequestParam String siteId)
|
||||
{
|
||||
return success(iSingleSiteService.getSiteMonitorTotalDataVo(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单站监控-设备监控-实时运行头部数据
|
||||
*/
|
||||
@ -51,27 +74,75 @@ public class EmsSiteMonitorController extends BaseController{
|
||||
* 单站监控-设备监控-实时运行曲线图数据
|
||||
*/
|
||||
@GetMapping("/runningGraph/storagePower")
|
||||
public AjaxResult getRunningGraphStorage(@RequestParam String siteId)
|
||||
public AjaxResult getRunningGraphStorage(RunningGraphRequest request,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate)
|
||||
{
|
||||
return success(iSingleSiteService.getRunningGraphStorage(siteId));
|
||||
SiteMonitorRuningInfoVo data = iSingleSiteService.getRunningGraphStorage(request);
|
||||
int deviceCount = data == null || data.getPcsPowerList() == null ? 0 : data.getPcsPowerList().size();
|
||||
log.info("{} storage, siteId={}, rawStartDate={}, rawEndDate={}, bindStartDate={}, bindEndDate={}, deviceCount={}",
|
||||
RUNNING_GRAPH_CTRL_DEBUG,
|
||||
request == null ? null : request.getSiteId(),
|
||||
startDate,
|
||||
endDate,
|
||||
request == null ? null : request.getStartDate(),
|
||||
request == null ? null : request.getEndDate(),
|
||||
deviceCount);
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@GetMapping("/runningGraph/stackAveTemp")
|
||||
public AjaxResult getRunningGraphStackTemp(@RequestParam String siteId)
|
||||
@GetMapping("/runningGraph/pcsMaxTemp")
|
||||
public AjaxResult getRunningGraphPcsMaxTemp(RunningGraphRequest request,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate)
|
||||
{
|
||||
return success(iSingleSiteService.getRunningGraphStackTemp(siteId));
|
||||
SiteMonitorRuningInfoVo data = iSingleSiteService.getRunningGraphPcsMaxTemp(request);
|
||||
int deviceCount = data == null || data.getPcsMaxTempList() == null ? 0 : data.getPcsMaxTempList().size();
|
||||
log.info("{} pcsMaxTemp, siteId={}, rawStartDate={}, rawEndDate={}, bindStartDate={}, bindEndDate={}, deviceCount={}",
|
||||
RUNNING_GRAPH_CTRL_DEBUG,
|
||||
request == null ? null : request.getSiteId(),
|
||||
startDate,
|
||||
endDate,
|
||||
request == null ? null : request.getStartDate(),
|
||||
request == null ? null : request.getEndDate(),
|
||||
deviceCount);
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@GetMapping("/runningGraph/batteryAveSoc")
|
||||
public AjaxResult getRunningGraphBatterySoc(@RequestParam String siteId)
|
||||
public AjaxResult getRunningGraphBatterySoc(RunningGraphRequest request,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate)
|
||||
{
|
||||
return success(iSingleSiteService.getRunningGraphBatterySoc(siteId));
|
||||
SiteMonitorRuningInfoVo data = iSingleSiteService.getRunningGraphBatterySoc(request);
|
||||
int pointCount = data == null || data.getBatteryAveSOCList() == null ? 0 : data.getBatteryAveSOCList().size();
|
||||
log.info("{} batteryAveSoc, siteId={}, rawStartDate={}, rawEndDate={}, bindStartDate={}, bindEndDate={}, pointCount={}",
|
||||
RUNNING_GRAPH_CTRL_DEBUG,
|
||||
request == null ? null : request.getSiteId(),
|
||||
startDate,
|
||||
endDate,
|
||||
request == null ? null : request.getStartDate(),
|
||||
request == null ? null : request.getEndDate(),
|
||||
pointCount);
|
||||
return success(data);
|
||||
}
|
||||
|
||||
@GetMapping("/runningGraph/batteryAveTemp")
|
||||
public AjaxResult getRunningGraphBatteryTemp(@RequestParam String siteId)
|
||||
public AjaxResult getRunningGraphBatteryTemp(RunningGraphRequest request,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate)
|
||||
{
|
||||
return success(iSingleSiteService.getRunningGraphBatteryTemp(siteId));
|
||||
SiteMonitorRuningInfoVo data = iSingleSiteService.getRunningGraphBatteryTemp(request);
|
||||
int pointCount = data == null || data.getBatteryAveTempList() == null ? 0 : data.getBatteryAveTempList().size();
|
||||
log.info("{} batteryAveTemp, siteId={}, rawStartDate={}, rawEndDate={}, bindStartDate={}, bindEndDate={}, pointCount={}",
|
||||
RUNNING_GRAPH_CTRL_DEBUG,
|
||||
request == null ? null : request.getSiteId(),
|
||||
startDate,
|
||||
endDate,
|
||||
request == null ? null : request.getStartDate(),
|
||||
request == null ? null : request.getEndDate(),
|
||||
pointCount);
|
||||
return success(data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,11 +212,25 @@ public class EmsSiteMonitorController extends BaseController{
|
||||
* 获取电池簇下面的单体电池数据
|
||||
*/
|
||||
@GetMapping("/getClusterDataInfoList")
|
||||
public TableDataInfo getClusterDataInfoList(@RequestParam String clusterDeviceId,@RequestParam String siteId)
|
||||
public TableDataInfo getClusterDataInfoList(@RequestParam String clusterDeviceId,@RequestParam String siteId,
|
||||
@RequestParam String stackDeviceId,@RequestParam String batteryId)
|
||||
{
|
||||
startPage();
|
||||
List<BatteryDataStatsListVo> list = iSingleSiteService.getClusterDataInfoList(clusterDeviceId,siteId);
|
||||
return getDataTable2(list);
|
||||
SiteBatteryDataList siteBatteryDataList = new SiteBatteryDataList();
|
||||
|
||||
// 单体电池数据
|
||||
List<BatteryDataStatsListVo> List = iSingleSiteService.getClusterDataInfoList(clusterDeviceId,siteId,stackDeviceId,batteryId);
|
||||
// 对batteryList进行分页处理
|
||||
List<BatteryDataStatsListVo> batteryList = paginateList(List);
|
||||
siteBatteryDataList.setBatteryList(batteryList);
|
||||
|
||||
// 封装分页信息
|
||||
TableDataInfo pageInfo = new TableDataInfo();
|
||||
pageInfo.setTotal(List.size());
|
||||
pageInfo.setRows(Arrays.asList(siteBatteryDataList));
|
||||
pageInfo.setCode(0);
|
||||
pageInfo.setMsg("查询成功");
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -165,4 +250,73 @@ public class EmsSiteMonitorController extends BaseController{
|
||||
{
|
||||
return success(iSingleSiteService.getAmmeterDataList(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 动环数据
|
||||
*/
|
||||
@GetMapping("/getDhDataList")
|
||||
public AjaxResult getDhDataList(@RequestParam String siteId)
|
||||
{
|
||||
return success(iSingleSiteService.getDhDataList(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 消防数据
|
||||
*/
|
||||
@GetMapping("/getXfDataList")
|
||||
public AjaxResult getXfDataList(@RequestParam String siteId)
|
||||
{
|
||||
return success(iSingleSiteService.getXfDataList(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* EMS数据
|
||||
*/
|
||||
@GetMapping("/getEmsDataList")
|
||||
public AjaxResult getEmsDataList(@RequestParam String siteId)
|
||||
{
|
||||
return success(iSingleSiteService.getEmsDataList(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单站监控-首页-点位展示
|
||||
* 储能功率、电网功率、负荷功率、光伏功率
|
||||
* SOC、SOH、电池平均温度
|
||||
*/
|
||||
@GetMapping("/getPointData")
|
||||
public AjaxResult getPointData(DateSearchRequest requestVo)
|
||||
{
|
||||
if (!StringUtils.isEmpty(requestVo.getSiteId())) {
|
||||
return success(iSingleSiteService.getPointData(requestVo));
|
||||
} else {
|
||||
return error("缺少必传项");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单站监控项目点位配置查询
|
||||
*/
|
||||
@GetMapping("/getProjectPointMapping")
|
||||
public AjaxResult getProjectPointMapping(@RequestParam String siteId)
|
||||
{
|
||||
return success(iEmsDeviceSettingService.getSiteMonitorProjectPointMapping(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单站监控项目展示数据查询(配置字段 + 字段值)
|
||||
*/
|
||||
@GetMapping("/getProjectDisplayData")
|
||||
public AjaxResult getProjectDisplayData(@RequestParam String siteId)
|
||||
{
|
||||
return success(iEmsDeviceSettingService.getSiteMonitorProjectDisplay(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单站监控项目展示数据写入
|
||||
*/
|
||||
@PostMapping("/saveProjectDisplayData")
|
||||
public AjaxResult saveProjectDisplayData(@RequestBody SiteMonitorDataSaveRequest request)
|
||||
{
|
||||
return AjaxResult.success(iEmsDeviceSettingService.saveSiteMonitorProjectData(request, getUsername()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,33 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.core.page.TableDataInfo;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.vo.AmmeterRevenueStatisListVo;
|
||||
import com.xzzn.ems.domain.vo.AmmeterStatisListVo;
|
||||
import com.xzzn.ems.domain.vo.ClusterStatisListVo;
|
||||
import com.xzzn.ems.domain.vo.DateSearchRequest;
|
||||
import com.xzzn.ems.domain.vo.StatisAmmeterDateRequest;
|
||||
import com.xzzn.ems.domain.vo.StatisClusterDateRequest;
|
||||
import com.xzzn.ems.domain.vo.WeatherSyncResultVo;
|
||||
import com.xzzn.ems.service.IEmsStatsReportService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.xzzn.ems.service.IEmsWeatherSyncService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 单站监控-统计报表
|
||||
*
|
||||
@ -29,6 +40,8 @@ public class EmsStatisticalReportController extends BaseController
|
||||
|
||||
@Autowired
|
||||
private IEmsStatsReportService ieEmsStatsReportService;
|
||||
@Autowired
|
||||
private IEmsWeatherSyncService iEmsWeatherSyncService;
|
||||
|
||||
/**
|
||||
* 概率统计-收益指标查询
|
||||
@ -58,8 +71,7 @@ public class EmsStatisticalReportController extends BaseController
|
||||
@GetMapping("/getPCSData")
|
||||
public AjaxResult getPCSData(DateSearchRequest requestVo)
|
||||
{
|
||||
if (!StringUtils.isEmpty(requestVo.getSiteId()) &&
|
||||
!StringUtils.isEmpty(requestVo.getDeviceId()) &&
|
||||
if (!StringUtils.isEmpty(requestVo.getSiteId())&&
|
||||
!StringUtils.isEmpty(requestVo.getDataType())) {
|
||||
return success(ieEmsStatsReportService.getPCSDataResult(requestVo));
|
||||
} else {
|
||||
@ -74,7 +86,6 @@ public class EmsStatisticalReportController extends BaseController
|
||||
public AjaxResult getStackData(DateSearchRequest requestVo)
|
||||
{
|
||||
if (!StringUtils.isEmpty(requestVo.getSiteId()) &&
|
||||
!StringUtils.isEmpty(requestVo.getDeviceId()) &&
|
||||
!StringUtils.isEmpty(requestVo.getDataType())) {
|
||||
return success(ieEmsStatsReportService.getStackDataResult(requestVo));
|
||||
} else {
|
||||
@ -100,7 +111,7 @@ public class EmsStatisticalReportController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* 概率统计-获取总表
|
||||
* 概率统计-获取站点下所有电表
|
||||
*/
|
||||
@GetMapping("/getLoadNameList")
|
||||
public AjaxResult getLoadNameList(String siteId)
|
||||
@ -116,13 +127,66 @@ public class EmsStatisticalReportController extends BaseController
|
||||
* 概率统计-电表报表
|
||||
*/
|
||||
@GetMapping("/getAmmeterData")
|
||||
public AjaxResult getAmmeterData(StatisAmmeterDateRequest requestVo)
|
||||
public TableDataInfo getAmmeterData(StatisAmmeterDateRequest requestVo)
|
||||
{
|
||||
if (!StringUtils.isEmpty(requestVo.getDeviceId())) {
|
||||
return success(ieEmsStatsReportService.getAmmeterDataResult(requestVo));
|
||||
} else {
|
||||
return error("缺少必传项");
|
||||
}
|
||||
startPage();
|
||||
List<AmmeterStatisListVo> dataList = ieEmsStatsReportService.getAmmeterDataResult(requestVo);
|
||||
return getDataTable(dataList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计报表-电表报表(直接基于 ems_daily_energy_data)
|
||||
*/
|
||||
@GetMapping("/getAmmeterDataFromDaily")
|
||||
public TableDataInfo getAmmeterDataFromDaily(StatisAmmeterDateRequest requestVo)
|
||||
{
|
||||
startPage();
|
||||
List<AmmeterStatisListVo> dataList = ieEmsStatsReportService.getAmmeterDataResult(requestVo);
|
||||
return getDataTable(dataList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出电表报表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:ammeterData:export')")
|
||||
@Log(title = "电表报表", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/exportAmmeterData")
|
||||
public void exportAmmeterData(HttpServletResponse response, StatisAmmeterDateRequest requestVo)
|
||||
{
|
||||
ieEmsStatsReportService.exportAmmeterData(response, requestVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出电表报表(直接基于 ems_daily_energy_data)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:ammeterData:export')")
|
||||
@Log(title = "电表报表", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/exportAmmeterDataFromDaily")
|
||||
public void exportAmmeterDataFromDaily(HttpServletResponse response, StatisAmmeterDateRequest requestVo)
|
||||
{
|
||||
ieEmsStatsReportService.exportAmmeterData(response, requestVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 概率统计-电表收益报表
|
||||
*/
|
||||
@GetMapping("/getAmmeterRevenueData")
|
||||
public TableDataInfo getAmmeterRevenueData(StatisAmmeterDateRequest requestVo)
|
||||
{
|
||||
startPage();
|
||||
List<AmmeterRevenueStatisListVo> dataList = ieEmsStatsReportService.getAmmeterRevenueDataResult(requestVo);
|
||||
return getDataTable(dataList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出收益报表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:ammeterRevenueData:export')")
|
||||
@Log(title = "收益报表", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/exportAmmeterRevenueData")
|
||||
public void exportAmmeterRevenueData(HttpServletResponse response, StatisAmmeterDateRequest requestVo)
|
||||
{
|
||||
ieEmsStatsReportService.exportAmmeterRevenueData(response, requestVo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,13 +195,26 @@ public class EmsStatisticalReportController extends BaseController
|
||||
@GetMapping("/getPowerData")
|
||||
public AjaxResult getPowerData(DateSearchRequest requestVo)
|
||||
{
|
||||
if (!StringUtils.isEmpty(requestVo.getSiteId())
|
||||
&& !StringUtils.isEmpty(requestVo.getDeviceId())
|
||||
&& !StringUtils.isEmpty(requestVo.getDataType())) {
|
||||
if (!StringUtils.isEmpty(requestVo.getSiteId())) {
|
||||
return success(ieEmsStatsReportService.getPowerDataList(requestVo));
|
||||
} else {
|
||||
return error("缺少必传项");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动触发天气同步
|
||||
*/
|
||||
@PostMapping("/syncWeatherByDateRange")
|
||||
public AjaxResult syncWeatherByDateRange(StatisAmmeterDateRequest requestVo)
|
||||
{
|
||||
if (StringUtils.isEmpty(requestVo.getSiteId())
|
||||
|| StringUtils.isEmpty(requestVo.getStartTime())
|
||||
|| StringUtils.isEmpty(requestVo.getEndTime())) {
|
||||
return error("缺少必传项: siteId/startTime/endTime");
|
||||
}
|
||||
WeatherSyncResultVo resultVo = iEmsWeatherSyncService.syncWeatherByDateRange(requestVo);
|
||||
return success(resultVo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsStrategyRuntimeConfig;
|
||||
import com.xzzn.ems.service.IEmsStrategyRuntimeConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 策略运行参数配置Controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/strategyRuntimeConfig")
|
||||
public class EmsStrategyRuntimeConfigController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IEmsStrategyRuntimeConfigService runtimeConfigService;
|
||||
|
||||
/**
|
||||
* 按站点ID获取策略运行参数
|
||||
*/
|
||||
@GetMapping("/getBySiteId")
|
||||
public AjaxResult getBySiteId(String siteId) {
|
||||
if (StringUtils.isEmpty(siteId)) {
|
||||
return error("缺少必填字段siteId");
|
||||
}
|
||||
return success(runtimeConfigService.getBySiteId(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存策略运行参数(按siteId新增/更新)
|
||||
*/
|
||||
@PostMapping("/save")
|
||||
public AjaxResult save(@RequestBody EmsStrategyRuntimeConfig config) {
|
||||
if (config == null || StringUtils.isEmpty(config.getSiteId())) {
|
||||
return error("缺少必填字段siteId");
|
||||
}
|
||||
config.setCreateBy(getUsername());
|
||||
config.setUpdateBy(getUsername());
|
||||
return toAjax(runtimeConfigService.saveBySiteId(config));
|
||||
}
|
||||
}
|
||||
@ -1,39 +1,53 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.xzzn.ems.domain.EmsMqttMessage;
|
||||
import com.xzzn.ems.service.IDDSDataProcessService;
|
||||
import com.xzzn.ems.service.IEmsMqttMessageService;
|
||||
import com.xzzn.ems.service.IFXXDataProcessService;
|
||||
import com.xzzn.common.constant.RedisKeyConstants;
|
||||
import com.xzzn.common.core.redis.RedisCache;
|
||||
import com.xzzn.common.enums.TopicHandleType;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsMqttTopicConfig;
|
||||
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||
import com.xzzn.ems.service.IDeviceDataProcessService;
|
||||
import com.xzzn.ems.service.IMqttSyncLogService;
|
||||
import com.xzzn.framework.manager.MqttLifecycleManager;
|
||||
import com.xzzn.framework.web.service.MqttPublisher;
|
||||
import com.xzzn.framework.web.service.MqttSubscriber;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@Service
|
||||
public class MqttMessageController implements MqttPublisher, MqttSubscriber {
|
||||
|
||||
|
||||
private static final Log log = LogFactory.getLog(MqttMessageController.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(MqttMessageController.class);
|
||||
|
||||
|
||||
private final MqttLifecycleManager mqttLifecycleManager;
|
||||
|
||||
@Autowired
|
||||
private IEmsMqttMessageService emsMqttMessageService;
|
||||
|
||||
@Autowired
|
||||
private IFXXDataProcessService fXXDataProcessService;
|
||||
private IDeviceDataProcessService deviceDataProcessService;
|
||||
|
||||
@Autowired
|
||||
private IDDSDataProcessService dDSDataProcessService;
|
||||
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||
@Autowired
|
||||
private IMqttSyncLogService iMqttSyncLogService;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
@Autowired
|
||||
public MqttMessageController(MqttLifecycleManager mqttLifecycleManager) {
|
||||
this.mqttLifecycleManager = mqttLifecycleManager;
|
||||
@ -41,50 +55,170 @@ public class MqttMessageController implements MqttPublisher, MqttSubscriber {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
List<EmsMqttTopicConfig> topicConfigList = emsMqttTopicConfigMapper.selectEmsMqttTopicConfigList(null);
|
||||
|
||||
if (CollectionUtils.isEmpty (topicConfigList)) {
|
||||
log.info ("未查询到任何 MQTT 主题配置,跳过订阅");
|
||||
return;
|
||||
}
|
||||
|
||||
for (EmsMqttTopicConfig topicConfig : topicConfigList) {
|
||||
String topic = topicConfig.getMqttTopic();
|
||||
if (StringUtils.isEmpty(topic)) {
|
||||
log.info ("主题配置 ID:" +topicConfig.getId() +"的 mqttTopic 为空,跳过订阅");
|
||||
continue;
|
||||
}
|
||||
|
||||
int qos = topicConfig.getQos() == null ? 1 : topicConfig.getQos();
|
||||
if (qos < 0 || qos > 2) {
|
||||
log.info ("主题:" + topic +"的 QoS值"+ qos + "不合法,自动调整为1");
|
||||
qos = 1;
|
||||
}
|
||||
|
||||
IMqttMessageListener listener = getMqttListenerByTopic(topic, topicConfig.getHandleType());
|
||||
subscribe(topic, qos, listener);
|
||||
}
|
||||
// 订阅奉贤系统状态主题
|
||||
subscribe("021_FXX_01_UP", 1, this::handleDeviceData);
|
||||
/*subscribe("021_FXX_01_UP", 1, this::handleDeviceData);
|
||||
subscribe("021_FXX_01_RECALL", 1, this::handleDeviceData);
|
||||
subscribe("021_FXX_01_DOWN", 1, this::handleDeviceData);
|
||||
subscribe("021_FXX_01", 1, this::handleSystemStatus);
|
||||
subscribe("021_FXX_01_ALARM_UP", 1, this::handleAlarmData);
|
||||
|
||||
// 订阅电动所系统状态主题
|
||||
subscribe("021_DDS_01_UP", 1, this::handleDeviceData);
|
||||
subscribe("021_DDS_01_RECALL", 1, this::handleDeviceData);
|
||||
subscribe("021_DDS_01_DOWN", 1, this::handleDeviceData);
|
||||
subscribe("021_DDS_01", 1, this::handleSystemStatus);
|
||||
subscribe("021_DDS_01", 1, this::handleSystemStatus);*/
|
||||
|
||||
}
|
||||
|
||||
private IMqttMessageListener getMqttListenerByTopic(String topic, String handleType) {
|
||||
TopicHandleType topicHandleType = TopicHandleType.getEnumByCode(handleType);
|
||||
switch (topicHandleType) {
|
||||
case DEVICE:
|
||||
return this::handleDeviceData;
|
||||
case DEVICE_ALARM:
|
||||
return this::handleAlarmData;
|
||||
case STRATEGY:
|
||||
if (topic.equals("FAULT_PROTECTION_PLAN_UP")) {
|
||||
return this::handleFaultProtPlanData;
|
||||
} else if (topic.equals("FAULT_ALARM_RECORD_UP")) {
|
||||
return this::handleFaultAlarmData;
|
||||
} else if (topic.equals("FAULT_PLAN_ISSUE_UP")) {
|
||||
return this::handleFaultPlanIssueData;
|
||||
} else if (topic.equals("DEVICE_CHANGE_LOG_UP")) {
|
||||
return this::handleDeviceChangeLogData;
|
||||
} else {
|
||||
return this::handleStrategyData;
|
||||
}
|
||||
default:
|
||||
log.warn("Unknown handle type: " + handleType + ", using default handler");
|
||||
return this::handleSystemStatus;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理系统状态消息
|
||||
private void handleSystemStatus(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
System.out.println("[SYSTEM] Status update: " + payload);
|
||||
|
||||
try {
|
||||
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process system status message: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理设备数据
|
||||
private void handleDeviceData(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
System.out.println("[DEVICE] data: " + payload);
|
||||
try {
|
||||
// 业务处理逻辑
|
||||
if (topic.startsWith("021_DDS")) {
|
||||
dDSDataProcessService.handleDdsData(payload);
|
||||
} else if (topic.startsWith("021_FXX")) {
|
||||
fXXDataProcessService.handleFxData(payload);
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
log.debug("[DEVICE] data: {}", payload);
|
||||
try {
|
||||
deviceDataProcessService.handleDeviceData(payload, getSiteIdByTopic(topic));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process device data message: {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
|
||||
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process system status message: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
// 处理告警数据
|
||||
private void handleAlarmData(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
try {
|
||||
deviceDataProcessService.handleAlarmData(payload, getSiteIdByTopic(topic));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process device alarm data message: {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private String getSiteIdByTopic(String topic) {
|
||||
String siteId = redisCache.getCacheObject(RedisKeyConstants.SITE_ID + topic);
|
||||
if (StringUtils.isEmpty(siteId)) {
|
||||
EmsMqttTopicConfig topicConfig = emsMqttTopicConfigMapper.selectOneByTopic(topic);
|
||||
siteId = topicConfig.getSiteId();
|
||||
redisCache.setCacheObject(RedisKeyConstants.SITE_ID + topic, siteId);
|
||||
}
|
||||
return siteId;
|
||||
}
|
||||
|
||||
// 处理运行策略数据:云端-本地
|
||||
private void handleStrategyData(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
try {
|
||||
iMqttSyncLogService.handleMqttStrategyData(payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process strategy data message: {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理设备保护告警策略数据:云端-本地
|
||||
private void handleFaultProtPlanData(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
try {
|
||||
iMqttSyncLogService.handleMqttPlanData(payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process fault plan data message: {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理保护策略告警信息:本地-云端
|
||||
private void handleFaultAlarmData(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
try {
|
||||
iMqttSyncLogService.handleFaultAlarmData(payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process fault plan alarm data message: {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理保护策略下发日志:本地-云端
|
||||
private void handleFaultPlanIssueData(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
try {
|
||||
iMqttSyncLogService.handleFaultPlanIssueData(payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process fault plan issue log message: {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理设备状态变更日志:本地-云端
|
||||
private void handleDeviceChangeLogData(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
try {
|
||||
iMqttSyncLogService.handleDeviceChangeLogData(payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process device change log message: {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,6 +248,4 @@ public class MqttMessageController implements MqttPublisher, MqttSubscriber {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,61 +1,61 @@
|
||||
# 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://122.51.194.184:13306/setri_ems?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: ems
|
||||
password: 12345678
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
enabled: false
|
||||
url:
|
||||
username:
|
||||
password:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置连接超时时间
|
||||
connectTimeout: 30000
|
||||
# 配置网络超时时间
|
||||
socketTimeout: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: xzzn
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
## 数据源配置
|
||||
#spring:
|
||||
# datasource:
|
||||
# type: com.alibaba.druid.pool.DruidDataSource
|
||||
# driverClassName: com.mysql.cj.jdbc.Driver
|
||||
# druid:
|
||||
# # 主库数据源
|
||||
# master:
|
||||
# url: jdbc:mysql://122.51.194.184:13306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: ems
|
||||
# password: 12345678
|
||||
# # 从库数据源
|
||||
# slave:
|
||||
# # 从数据源开关/默认关闭
|
||||
# enabled: false
|
||||
# url:
|
||||
# username:
|
||||
# password:
|
||||
# # 初始连接数
|
||||
# initialSize: 5
|
||||
# # 最小连接池数量
|
||||
# minIdle: 10
|
||||
# # 最大连接池数量
|
||||
# maxActive: 20
|
||||
# # 配置获取连接等待超时的时间
|
||||
# maxWait: 60000
|
||||
# # 配置连接超时时间
|
||||
# connectTimeout: 30000
|
||||
# # 配置网络超时时间
|
||||
# socketTimeout: 60000
|
||||
# # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
# timeBetweenEvictionRunsMillis: 60000
|
||||
# # 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
# minEvictableIdleTimeMillis: 300000
|
||||
# # 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
# maxEvictableIdleTimeMillis: 900000
|
||||
# # 配置检测连接是否有效
|
||||
# validationQuery: SELECT 1 FROM DUAL
|
||||
# testWhileIdle: true
|
||||
# testOnBorrow: false
|
||||
# testOnReturn: false
|
||||
# webStatFilter:
|
||||
# enabled: true
|
||||
# statViewServlet:
|
||||
# enabled: true
|
||||
# # 设置白名单,不填则允许所有访问
|
||||
# allow:
|
||||
# url-pattern: /druid/*
|
||||
# # 控制台管理用户名和密码
|
||||
# login-username: xzzn
|
||||
# login-password: 123456
|
||||
# filter:
|
||||
# stat:
|
||||
# enabled: true
|
||||
# # 慢SQL记录
|
||||
# log-slow-sql: true
|
||||
# slow-sql-millis: 1000
|
||||
# merge-sql: true
|
||||
# wall:
|
||||
# config:
|
||||
# multi-statement-allow: true
|
||||
214
ems-admin/src/main/resources/application-local.yml
Normal file
214
ems-admin/src/main/resources/application-local.yml
Normal file
@ -0,0 +1,214 @@
|
||||
# 项目相关配置
|
||||
xzzn:
|
||||
# 名称
|
||||
name: EMS
|
||||
# 版本
|
||||
version: 0.0.1
|
||||
# 版权年份
|
||||
copyrightYear: 2025
|
||||
# 文件路径 示例( Windows配置D:/xzzn/uploadPath,Linux配置 /home/xzzn/uploadPath)
|
||||
profile: ../uploadPath
|
||||
# 获取ip地址开关
|
||||
addressEnabled: false
|
||||
# 验证码类型 math 数字计算 char 字符验证
|
||||
captchaType: math
|
||||
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 8089
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
tomcat:
|
||||
# tomcat的URI编码
|
||||
uri-encoding: UTF-8
|
||||
# 连接数满后的排队数,默认为100
|
||||
accept-count: 1000
|
||||
threads:
|
||||
# tomcat最大线程数,默认为200
|
||||
max: 800
|
||||
# Tomcat启动初始化的线程数,默认值10
|
||||
min-spare: 100
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
com.xzzn: info
|
||||
org.springframework: warn
|
||||
|
||||
# 用户配置
|
||||
user:
|
||||
password:
|
||||
# 密码最大错误次数
|
||||
maxRetryCount: 5
|
||||
# 密码锁定时间(默认10分钟)
|
||||
lockTime: 10
|
||||
|
||||
# Spring配置
|
||||
spring:
|
||||
# 资源信息
|
||||
messages:
|
||||
# 国际化资源文件路径
|
||||
basename: i18n/messages
|
||||
# 文件上传
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件大小
|
||||
max-file-size: 10MB
|
||||
# 设置总上传的文件大小
|
||||
max-request-size: 20MB
|
||||
# 服务模块
|
||||
devtools:
|
||||
restart:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# redis 配置
|
||||
redis:
|
||||
# 地址
|
||||
host: 127.0.0.1
|
||||
# 端口,默认为6379
|
||||
port: 6379
|
||||
# 数据库索引
|
||||
database: 0
|
||||
# 密码
|
||||
password: 12345678
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 8
|
||||
# 连接池的最大数据库连接数
|
||||
max-active: 8
|
||||
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 数据源配置
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://127.0.0.1:3306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: ems
|
||||
password: Aa112211!
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
enabled: false
|
||||
url:
|
||||
username:
|
||||
password:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置连接超时时间
|
||||
connectTimeout: 30000
|
||||
# 配置网络超时时间
|
||||
socketTimeout: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: xzzn
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
|
||||
# token配置
|
||||
token:
|
||||
# 令牌自定义标识
|
||||
header: Authorization
|
||||
# 令牌密钥
|
||||
secret: abcdefghijklmnopqrstuvwxyz
|
||||
# 令牌有效期(默认30分钟)
|
||||
expireTime: 30
|
||||
|
||||
# MyBatis配置
|
||||
mybatis:
|
||||
# 搜索指定包别名
|
||||
typeAliasesPackage: com.xzzn.**.domain
|
||||
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||
# 加载全局的配置文件
|
||||
configLocation: classpath:mybatis/mybatis-config.xml
|
||||
|
||||
# PageHelper分页插件
|
||||
pagehelper:
|
||||
helperDialect: mysql
|
||||
supportMethodsArguments: true
|
||||
params: count=countSql
|
||||
|
||||
# Swagger配置
|
||||
swagger:
|
||||
# 是否开启swagger
|
||||
enabled: true
|
||||
# 请求前缀
|
||||
pathMapping: /dev-api
|
||||
|
||||
# 防止XSS攻击
|
||||
xss:
|
||||
# 过滤开关
|
||||
enabled: true
|
||||
# 排除链接(多个用逗号分隔)
|
||||
excludes: /system/notice
|
||||
# 匹配链接
|
||||
urlPatterns: /system/*,/monitor/*,/tool/*
|
||||
|
||||
mqtt:
|
||||
broker.url: tcp://121.5.164.6:1883
|
||||
client.id: ems-cloud
|
||||
username: dmbroker
|
||||
password: qwer1234
|
||||
connection-timeout: 15
|
||||
keep-alive-interval: 30
|
||||
automatic-reconnect: true
|
||||
topic: 021_DDS_01_UP
|
||||
siteId: 021_DDS_01
|
||||
|
||||
modbus:
|
||||
pool:
|
||||
max-total: 20
|
||||
max-idle: 10
|
||||
min-idle: 3
|
||||
poll:
|
||||
interval: "0 */5 * * * *" # 5分钟间隔
|
||||
timeout: 30000 # 30秒超时
|
||||
|
||||
weather:
|
||||
api:
|
||||
enabled: true
|
||||
base-url: https://archive-api.open-meteo.com/v1/archive
|
||||
api-key:
|
||||
timezone: Asia/Shanghai
|
||||
214
ems-admin/src/main/resources/application-prod.yml
Normal file
214
ems-admin/src/main/resources/application-prod.yml
Normal file
@ -0,0 +1,214 @@
|
||||
# 项目相关配置
|
||||
xzzn:
|
||||
# 名称
|
||||
name: EMS
|
||||
# 版本
|
||||
version: 0.0.1
|
||||
# 版权年份
|
||||
copyrightYear: 2025
|
||||
# 文件路径 示例( Windows配置D:/xzzn/uploadPath,Linux配置 /home/xzzn/uploadPath)
|
||||
profile: /home/xzzn/uploadPath
|
||||
# 获取ip地址开关
|
||||
addressEnabled: false
|
||||
# 验证码类型 math 数字计算 char 字符验证
|
||||
captchaType: math
|
||||
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080
|
||||
port: 8089
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
tomcat:
|
||||
# tomcat的URI编码
|
||||
uri-encoding: UTF-8
|
||||
# 连接数满后的排队数,默认为100
|
||||
accept-count: 1000
|
||||
threads:
|
||||
# tomcat最大线程数,默认为200
|
||||
max: 800
|
||||
# Tomcat启动初始化的线程数,默认值10
|
||||
min-spare: 100
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
com.xzzn: info
|
||||
org.springframework: warn
|
||||
|
||||
# 用户配置
|
||||
user:
|
||||
password:
|
||||
# 密码最大错误次数
|
||||
maxRetryCount: 5
|
||||
# 密码锁定时间(默认10分钟)
|
||||
lockTime: 10
|
||||
|
||||
# Spring配置
|
||||
spring:
|
||||
# 资源信息
|
||||
messages:
|
||||
# 国际化资源文件路径
|
||||
basename: i18n/messages
|
||||
# 文件上传
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件大小
|
||||
max-file-size: 10MB
|
||||
# 设置总上传的文件大小
|
||||
max-request-size: 20MB
|
||||
# 服务模块
|
||||
devtools:
|
||||
restart:
|
||||
# 热部署开关
|
||||
enabled: true
|
||||
# redis 配置
|
||||
redis:
|
||||
# 地址
|
||||
host: 172.17.0.9
|
||||
# 端口,默认为6379
|
||||
port: 6379
|
||||
# 数据库索引
|
||||
database: 0
|
||||
# 密码
|
||||
password: 12345678
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池中的最小空闲连接
|
||||
min-idle: 0
|
||||
# 连接池中的最大空闲连接
|
||||
max-idle: 8
|
||||
# 连接池的最大数据库连接数
|
||||
max-active: 8
|
||||
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 数据源配置
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://172.17.0.13:3306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: ems
|
||||
password: Aa112211!
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
enabled: false
|
||||
url:
|
||||
username:
|
||||
password:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置连接超时时间
|
||||
connectTimeout: 30000
|
||||
# 配置网络超时时间
|
||||
socketTimeout: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: xzzn
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
|
||||
# token配置
|
||||
token:
|
||||
# 令牌自定义标识
|
||||
header: Authorization
|
||||
# 令牌密钥
|
||||
secret: abcdefghijklmnopqrstuvwxyz
|
||||
# 令牌有效期(默认30分钟)
|
||||
expireTime: 30
|
||||
|
||||
# MyBatis配置
|
||||
mybatis:
|
||||
# 搜索指定包别名
|
||||
typeAliasesPackage: com.xzzn.**.domain
|
||||
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
||||
# 加载全局的配置文件
|
||||
configLocation: classpath:mybatis/mybatis-config.xml
|
||||
|
||||
# PageHelper分页插件
|
||||
pagehelper:
|
||||
helperDialect: mysql
|
||||
supportMethodsArguments: true
|
||||
params: count=countSql
|
||||
|
||||
# Swagger配置
|
||||
swagger:
|
||||
# 是否开启swagger
|
||||
enabled: true
|
||||
# 请求前缀
|
||||
pathMapping: /dev-api
|
||||
|
||||
# 防止XSS攻击
|
||||
xss:
|
||||
# 过滤开关
|
||||
enabled: true
|
||||
# 排除链接(多个用逗号分隔)
|
||||
excludes: /system/notice
|
||||
# 匹配链接
|
||||
urlPatterns: /system/*,/monitor/*,/tool/*
|
||||
|
||||
mqtt:
|
||||
broker.url: tcp://121.5.164.6:1883
|
||||
client.id: ems-cloud
|
||||
username: dmbroker
|
||||
password: qwer1234
|
||||
connection-timeout: 15
|
||||
keep-alive-interval: 30
|
||||
automatic-reconnect: true
|
||||
topic:
|
||||
siteId:
|
||||
|
||||
modbus:
|
||||
pool:
|
||||
max-total: 20
|
||||
max-idle: 10
|
||||
min-idle: 3
|
||||
poll:
|
||||
interval: "0 */5 * * * *" # 5分钟间隔
|
||||
timeout: 30000 # 30秒超时
|
||||
|
||||
weather:
|
||||
api:
|
||||
enabled: true
|
||||
base-url: https://archive-api.open-meteo.com/v1/archive
|
||||
api-key:
|
||||
timezone: Asia/Shanghai
|
||||
@ -87,6 +87,66 @@ spring:
|
||||
max-active: 8
|
||||
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
# 数据源配置
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://122.51.194.184:13306/setri_ems?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: ems
|
||||
password: 12345678
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
enabled: false
|
||||
url:
|
||||
username:
|
||||
password:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置连接超时时间
|
||||
connectTimeout: 30000
|
||||
# 配置网络超时时间
|
||||
socketTimeout: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1 FROM DUAL
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: xzzn
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
|
||||
# token配置
|
||||
token:
|
||||
@ -136,6 +196,22 @@ mqtt:
|
||||
connection-timeout: 15
|
||||
keep-alive-interval: 30
|
||||
automatic-reconnect: true
|
||||
topic:
|
||||
siteId:
|
||||
|
||||
influxdb:
|
||||
enabled: true
|
||||
url: http://122.51.194.184:8086/
|
||||
api-token: F2XcmBzZsWcz90ikU2_t7UXY2fzWuf2ruVp1BkusNkIS_gwrQZuiaIjl33XQMQajm7vSI6TQSRnpPSx5CXThlA==
|
||||
write-method: POST
|
||||
read-method: GET
|
||||
write-path: /api/v2/write
|
||||
query-path: /query
|
||||
org: ems
|
||||
bucket: point_data
|
||||
database: ems_point_data
|
||||
retention-policy: autogen
|
||||
measurement: mqtt_point_data
|
||||
|
||||
modbus:
|
||||
pool:
|
||||
@ -145,3 +221,10 @@ modbus:
|
||||
poll:
|
||||
interval: "0 */5 * * * *" # 5分钟间隔
|
||||
timeout: 30000 # 30秒超时
|
||||
|
||||
weather:
|
||||
api:
|
||||
enabled: true
|
||||
base-url: https://archive-api.open-meteo.com/v1/archive
|
||||
api-key:
|
||||
timezone: Asia/Shanghai
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="/etc/xzzn/logs" />
|
||||
<property name="log.path" value="logs" />
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
|
||||
@ -119,6 +119,12 @@
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- modbus4j (保留兼容) -->
|
||||
<dependency>
|
||||
<groupId>com.infiniteautomation</groupId>
|
||||
<artifactId>modbus4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,11 @@
|
||||
package com.xzzn.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 标记:调用insert后需要同步数据到其他服务器
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SyncAfterInsert {
|
||||
}
|
||||
@ -170,4 +170,9 @@ public class Constants
|
||||
*/
|
||||
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
|
||||
"org.springframework", "org.apache", "com.xzzn.common.utils.file", "com.xzzn.common.config", "com.xzzn.generator" };
|
||||
|
||||
/**
|
||||
* 昨日充放电最晚数据有效期
|
||||
*/
|
||||
public static final Integer DATE_VALID_TIME = 1;
|
||||
}
|
||||
|
||||
@ -42,6 +42,11 @@ public class RedisKeyConstants
|
||||
*/
|
||||
public static final String DH = "DH_";
|
||||
|
||||
/**
|
||||
* 消防数据 redis key
|
||||
*/
|
||||
public static final String XF = "XF_";
|
||||
|
||||
/**
|
||||
* 电池组 redis key
|
||||
*/
|
||||
@ -51,4 +56,88 @@ public class RedisKeyConstants
|
||||
* BMSD原始数据 redis key
|
||||
*/
|
||||
public static final String ORIGINAL_BMSD = "BMSD_";
|
||||
|
||||
/**
|
||||
* 冷却数据 redis key
|
||||
*/
|
||||
public static final String COOLING = "COOLING_";
|
||||
|
||||
/**
|
||||
* EMS数据 redis key
|
||||
*/
|
||||
public static final String EMS = "EMS_";
|
||||
|
||||
/**
|
||||
* 点位匹配数据 redis key
|
||||
*/
|
||||
public static final String POINT_MATCH = "POINT_MATCH_";
|
||||
|
||||
/**
|
||||
* 点位枚举匹配数据 redis key
|
||||
*/
|
||||
public static final String POINT_ENUM_MATCH = "POINT_ENUM_MATCH_";
|
||||
|
||||
/**
|
||||
* topic对应站点ID redis key
|
||||
*/
|
||||
public static final String SITE_ID = "SITE_ID_";
|
||||
|
||||
/**
|
||||
* 存放单个设备同步过来的原始数据-最晚一次数据
|
||||
*/
|
||||
public static final String ORIGINAL_MQTT_DATA = "MQTT_";
|
||||
|
||||
/**
|
||||
* 存放单个设备同步过来的告警点位原始数据-最晚一次数据
|
||||
*/
|
||||
public static final String ORIGINAL_MQTT_DATA_ALARM = "MQTT_ALARM_";
|
||||
|
||||
/** 存放订阅失败告警信息 */
|
||||
public static final String TOPIC_FAILED_ALRAM_RECORD = "topic_failed_";
|
||||
/** topic 内没有数据设备维度告警 */
|
||||
public static final String TOPIC_EMPTY_ALARM_RECORD = "topic_empty_";
|
||||
/** modbus读取 没有数据设备维度告警 */
|
||||
public static final String MODBUS_EMPTY_ALARM_RECORD = "modbus_empty_";
|
||||
/** modbus读取 设备离线告警 */
|
||||
public static final String MODBUS_OFFLINE_ALARM_RECORD = "modbus_offline_";
|
||||
|
||||
/** 设备信息初始化 */
|
||||
public static final String INIT_DEVICE_INFO = "init_device_info";
|
||||
|
||||
/** 设备配置缓存(按站点+设备) */
|
||||
public static final String DEVICE_SETTING = "DEVICE_SETTING_";
|
||||
/** 告警匹配信息 */
|
||||
public static final String ALARM_MATCH_INFO = "alarm_message_info";
|
||||
|
||||
/** 现有的告警数据 */
|
||||
public static final String LATEST_ALARM_RECORD = "LATEST_ALARM_RECORD";
|
||||
|
||||
/** 预存电价时间配置 */
|
||||
public static final String ENERGY_PRICE_TIME = "energy_price_time_";
|
||||
|
||||
/** dds昨日累计总收益 */
|
||||
public static final String DDS_TOTAL_REVENUE = "total_revenue_";
|
||||
/** fx实时总收益和当日实时收益 */
|
||||
public static final String FXX_REALTIME_REVENUE = "realtime_revenue_";
|
||||
|
||||
/** 每个设备最新数据-设置失效时间-判断是否正常同步数据 */
|
||||
public static final String SYNC_DATA= "SYNC_DATA_";
|
||||
|
||||
/** 每个设备最新数据-设置失效时间-判断是否正常同步数据 */
|
||||
public static final String SYNC_DATA_ALARM = "SYNC_DATA_ALARM_";
|
||||
|
||||
/** 点位配置缓存(按站点+设备) */
|
||||
public static final String POINT_CONFIG_DEVICE = "POINT_CONFIG_DEVICE_";
|
||||
|
||||
/** 点位配置缓存(按站点+pointId) */
|
||||
public static final String POINT_CONFIG_POINT = "POINT_CONFIG_POINT_";
|
||||
|
||||
/** 单站监控最新数据(按站点+模块) */
|
||||
public static final String SITE_MONITOR_LATEST = "SITE_MONITOR_LATEST_";
|
||||
|
||||
/** 单站监控点位映射(按站点) */
|
||||
public static final String SITE_MONITOR_POINT_MATCH = "SITE_MONITOR_POINT_MATCH_";
|
||||
|
||||
/** 站点保护约束(按站点) */
|
||||
public static final String PROTECTION_CONSTRAINT = "PROTECTION_CONSTRAINT_";
|
||||
}
|
||||
|
||||
@ -208,28 +208,69 @@ public class BaseController
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
protected TableDataInfo getDataTable2(List<?> list)
|
||||
{
|
||||
List<?> subList = new ArrayList<>();
|
||||
// 分页梳理
|
||||
// 1. 处理原列表为null的情况,避免空指针
|
||||
List<?> targetList = (list == null) ? Collections.emptyList() : list;
|
||||
List<?> subList;
|
||||
|
||||
// 2. 获取分页参数
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
int pageNum = pageDomain.getPageNum();
|
||||
int pageSize = pageDomain.getPageSize();
|
||||
|
||||
// 3. 判断分页参数是否有效(pageNum和pageSize均为正数时才分页)
|
||||
if (pageNum > 0 && pageSize > 0) {
|
||||
// 计算分页起始和结束索引
|
||||
int startIndex = (pageNum - 1) * pageSize;
|
||||
int endIndex = Math.min(startIndex + pageSize, list.size());
|
||||
// 防止越界
|
||||
if (startIndex >= list.size()) {
|
||||
subList = Collections.emptyList();
|
||||
// 计算起始索引(确保不小于0)
|
||||
int startIndex = Math.max((pageNum - 1) * pageSize, 0);
|
||||
|
||||
// 关键修复:若起始索引已超出列表大小,直接返回空列表
|
||||
if (startIndex >= targetList.size()) {
|
||||
subList = Collections.emptyList();
|
||||
} else {
|
||||
// 计算结束索引(不超过列表大小,且不小于起始索引)
|
||||
int endIndex = Math.min(startIndex + pageSize, targetList.size());
|
||||
endIndex = Math.max(endIndex, startIndex); // 防止endIndex < startIndex
|
||||
|
||||
// 截取子列表(转换为新ArrayList,避免视图依赖)
|
||||
subList = new ArrayList<>(targetList.subList(startIndex, endIndex));
|
||||
}
|
||||
// 截取当前页数据
|
||||
subList = list.subList(startIndex, endIndex);
|
||||
} else {
|
||||
// 分页参数无效时,返回全部数据
|
||||
subList = new ArrayList<>(targetList);
|
||||
}
|
||||
|
||||
// 4. 封装返回结果
|
||||
TableDataInfo rspData = new TableDataInfo();
|
||||
rspData.setCode(HttpStatus.SUCCESS);
|
||||
rspData.setMsg("查询成功");
|
||||
rspData.setRows(subList);
|
||||
rspData.setTotal(list.size());
|
||||
rspData.setTotal(targetList.size());
|
||||
return rspData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用分页工具方法
|
||||
*/
|
||||
protected <T> List<T> paginateList(List<T> sourceList) {
|
||||
if (sourceList == null || sourceList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 分页梳理
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
int pageNum = pageDomain.getPageNum();
|
||||
int pageSize = pageDomain.getPageSize();
|
||||
int startIndex = 0;
|
||||
int endIndex = sourceList.size();
|
||||
if (pageNum > 0 && pageSize > 0) {
|
||||
// 计算起始索引(处理页码小于1的情况)
|
||||
startIndex = Math.max((pageNum - 1) * pageSize, 0);
|
||||
// 计算结束索引(处理超出列表长度的情况)
|
||||
endIndex = Math.min(startIndex + pageSize, sourceList.size());
|
||||
}
|
||||
// 防止越界
|
||||
if (startIndex >= sourceList.size()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 截取分页数据
|
||||
return sourceList.subList(startIndex, endIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,6 +92,8 @@ public class SysUser extends BaseEntity
|
||||
/** 角色ID */
|
||||
private Long roleId;
|
||||
|
||||
private String belongSite;
|
||||
|
||||
public SysUser()
|
||||
{
|
||||
|
||||
@ -310,6 +312,14 @@ public class SysUser extends BaseEntity
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
public String getBelongSite() {
|
||||
return belongSite;
|
||||
}
|
||||
|
||||
public void setBelongSite(String belongSite) {
|
||||
this.belongSite = belongSite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
@ -332,7 +342,7 @@ public class SysUser extends BaseEntity
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.append("dept", getDept())
|
||||
.append("belongSite", getBelongSite())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,111 @@
|
||||
package com.xzzn.common.core.modbus;
|
||||
|
||||
import com.serotonin.modbus4j.ModbusFactory;
|
||||
import com.serotonin.modbus4j.ModbusMaster;
|
||||
import com.serotonin.modbus4j.ip.IpParameters;
|
||||
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Modbus连接管理器
|
||||
* 使用长连接模式,维护连接缓存,复用已有连接
|
||||
*/
|
||||
@Component
|
||||
public class Modbus4jConnectionManager {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Modbus4jConnectionManager.class);
|
||||
|
||||
private final ModbusFactory modbusFactory = new ModbusFactory();
|
||||
|
||||
private final Map<String, ModbusMaster> connectionCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 获取或创建连接(长连接模式)
|
||||
*/
|
||||
public ModbusMaster borrowMaster(DeviceConfig config) throws Exception {
|
||||
String key = buildConnectionKey(config);
|
||||
ModbusMaster master = connectionCache.get(key);
|
||||
|
||||
if (master == null) {
|
||||
synchronized (this) {
|
||||
master = connectionCache.get(key);
|
||||
if (master == null) {
|
||||
master = createNewConnection(config);
|
||||
connectionCache.put(key, master);
|
||||
logger.info("创建新Modbus长连接: {}:{}", config.getHost(), config.getPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return master;
|
||||
}
|
||||
|
||||
/**
|
||||
* 归还连接(长连接模式,不关闭)
|
||||
*/
|
||||
public void returnMaster(DeviceConfig config, ModbusMaster master) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 废弃连接(发生异常时关闭并移除)
|
||||
*/
|
||||
public void invalidateMaster(DeviceConfig config, ModbusMaster master) {
|
||||
String key = buildConnectionKey(config);
|
||||
connectionCache.remove(key);
|
||||
destroyMaster(master, config);
|
||||
logger.warn("废弃并移除Modbus连接: {}:{}", config.getHost(), config.getPort());
|
||||
}
|
||||
|
||||
private ModbusMaster createNewConnection(DeviceConfig config) throws Exception {
|
||||
IpParameters params = new IpParameters();
|
||||
params.setHost(config.getHost());
|
||||
params.setPort(config.getPort());
|
||||
params.setEncapsulated(false);
|
||||
|
||||
ModbusMaster master = modbusFactory.createTcpMaster(params, true);
|
||||
master.init();
|
||||
|
||||
return master;
|
||||
}
|
||||
|
||||
private void destroyMaster(ModbusMaster master, DeviceConfig config) {
|
||||
if (master != null) {
|
||||
try {
|
||||
master.destroy();
|
||||
logger.debug("已关闭Modbus连接: {}:{}", config.getHost(), config.getPort());
|
||||
} catch (Exception e) {
|
||||
logger.warn("关闭Modbus连接异常: {}:{}", config.getHost(), config.getPort(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildConnectionKey(DeviceConfig config) {
|
||||
return config.getHost() + ":" + config.getPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭所有连接
|
||||
*/
|
||||
public void closeAllConnections() {
|
||||
for (Map.Entry<String, ModbusMaster> entry : connectionCache.entrySet()) {
|
||||
try {
|
||||
if (entry.getValue() != null) {
|
||||
entry.getValue().destroy();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("关闭Modbus连接异常: {}", entry.getKey(), e);
|
||||
}
|
||||
}
|
||||
connectionCache.clear();
|
||||
logger.info("已关闭所有Modbus连接");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,486 @@
|
||||
package com.xzzn.common.core.modbus;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.serotonin.modbus4j.BatchRead;
|
||||
import com.serotonin.modbus4j.BatchResults;
|
||||
import com.serotonin.modbus4j.ModbusMaster;
|
||||
import com.serotonin.modbus4j.code.DataType;
|
||||
import com.serotonin.modbus4j.exception.ErrorResponseException;
|
||||
import com.serotonin.modbus4j.exception.ModbusTransportException;
|
||||
import com.serotonin.modbus4j.locator.BaseLocator;
|
||||
import com.serotonin.modbus4j.msg.WriteCoilRequest;
|
||||
import com.serotonin.modbus4j.msg.WriteCoilResponse;
|
||||
import com.serotonin.modbus4j.msg.WriteCoilsRequest;
|
||||
import com.serotonin.modbus4j.msg.WriteCoilsResponse;
|
||||
import com.serotonin.modbus4j.msg.WriteRegisterRequest;
|
||||
import com.serotonin.modbus4j.msg.WriteRegisterResponse;
|
||||
import com.serotonin.modbus4j.msg.WriteRegistersRequest;
|
||||
import com.serotonin.modbus4j.msg.WriteRegistersResponse;
|
||||
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
||||
import com.xzzn.common.core.modbus.domain.TagConfig;
|
||||
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
|
||||
import com.xzzn.common.core.redis.RedisCache;
|
||||
import com.xzzn.common.enums.ModBusType;
|
||||
import com.xzzn.common.enums.RegisterType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static com.xzzn.common.enums.RegisterType.COIL;
|
||||
import static com.xzzn.common.enums.RegisterType.DISCRETE_INPUT;
|
||||
|
||||
@Service
|
||||
public class ModbusProcessor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ModbusProcessor.class);
|
||||
@Value("${modbus.read-timeout:8000}")
|
||||
private int readTimeout;
|
||||
@Value("${modbus.write-timeout:5000}")
|
||||
private int writeTimeout;
|
||||
@Value("${modbus.read-retries:1}")
|
||||
private int readRetries;
|
||||
@Value("${modbus.write-retries:1}")
|
||||
private int writeRetries;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
@Autowired
|
||||
private Modbus4jConnectionManager connectionManager;
|
||||
|
||||
public boolean writeDataToDevice(DeviceConfig config) {
|
||||
logger.info("writeDataToDevice: {}", JSON.toJSONString(config));
|
||||
ModbusMaster master = null;
|
||||
boolean result = false;
|
||||
boolean hasError = false;
|
||||
try {
|
||||
master = connectionManager.borrowMaster(config);
|
||||
master.setTimeout(writeTimeout);
|
||||
master.setRetries(0);
|
||||
result = writeTagValue(master, config, config.getWriteTags());
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to borrow connection or write to devices '{}'", config.getDeviceName(), e);
|
||||
hasError = true;
|
||||
}
|
||||
finally {
|
||||
if (master != null) {
|
||||
if (hasError) {
|
||||
// 发生异常时废弃连接,下次重新创建
|
||||
connectionManager.invalidateMaster(config, master);
|
||||
} else {
|
||||
// 正常时归还连接
|
||||
connectionManager.returnMaster(config, master);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean writeDataToDeviceWithRetry(DeviceConfig config) {
|
||||
logger.info("writeDataToDevice: {}", JSON.toJSONString(config));
|
||||
ModbusMaster master = null;
|
||||
boolean result;
|
||||
|
||||
try {
|
||||
master = connectionManager.borrowMaster(config);
|
||||
master.setTimeout(writeTimeout); // 设置超时时间
|
||||
master.setRetries(0);
|
||||
|
||||
// 使用重试装饰器
|
||||
ModbusMaster finalMaster = master;
|
||||
result = RetryableModbusOperation.executeWithRetry(() -> {
|
||||
return writeTagValue(finalMaster, config, config.getWriteTags());
|
||||
}, writeRetries); // 最大重试次数由配置控制
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to borrow connection or write to devices '{}'", config.getDeviceName(), e);
|
||||
result = false;
|
||||
} finally {
|
||||
if (master != null) {
|
||||
connectionManager.returnMaster(config, master);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean writeTagValue(ModbusMaster master, DeviceConfig config, List<WriteTagConfig> tags) {
|
||||
tags.forEach(tag -> {
|
||||
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
|
||||
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
|
||||
int address = convertAddress(tag.getAddress());
|
||||
RegisterType registerType = type.get(firstDigit);
|
||||
logger.info("Register type: {}, address: {}, firstDigit: {}", registerType, tag.getAddress(), firstDigit);
|
||||
switch (registerType) {
|
||||
case COIL: {
|
||||
boolean result = writeCoilRequest(master, config.getSlaveId(), address, Boolean.parseBoolean(String.valueOf(tag.getValue())));
|
||||
tag.setWrite(result);
|
||||
break;
|
||||
}
|
||||
case HOLDING_REGISTER: {
|
||||
Number value = Double.parseDouble(String.valueOf(tag.getValue()));
|
||||
double doubleValue = value.doubleValue();
|
||||
// 检查是否在16位有符号整数范围内
|
||||
if (doubleValue < -32768 || doubleValue > 32767) {
|
||||
logger.warn("Value {} out of range for 16-bit signed register at address {}", doubleValue, address);
|
||||
}
|
||||
boolean result = writeRegisterRequest(master, config.getSlaveId(), address, (int) doubleValue);
|
||||
tag.setWrite(result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logger.error("Unsupported register type: {}", registerType);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
List<WriteTagConfig> collect = tags.stream().filter(tag -> !Objects.equals(tag.isWrite(), true)).collect(Collectors.toList());
|
||||
if (!collect.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean writeCoilRequest(ModbusMaster master, int slaveId, int address, boolean value) {
|
||||
try {
|
||||
WriteCoilRequest request = new WriteCoilRequest(slaveId, address, value);
|
||||
WriteCoilResponse response = (WriteCoilResponse)master.send(request);
|
||||
if (response.isException()) {
|
||||
logger.info("Write coil failed: " + response.getExceptionMessage());
|
||||
} else {
|
||||
logger.info("Write coil successful");
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to write coil value '{}' to address '{}'", value, address, e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void writeCoilsRequest(ModbusMaster master, int slaveId, int address, boolean[] values) {
|
||||
try {
|
||||
WriteCoilsRequest request = new WriteCoilsRequest(slaveId, address, values);
|
||||
WriteCoilsResponse response = (WriteCoilsResponse)master.send(request);
|
||||
if (response.isException()) {
|
||||
logger.info("Write coils failed: " + response.getExceptionMessage());
|
||||
} else {
|
||||
logger.info("Write coils successful");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to write coils value '{}' to address '{}'", values, address, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean writeRegisterRequest(ModbusMaster master, int slaveId, int address, int value) {
|
||||
try {
|
||||
WriteRegisterRequest request = new WriteRegisterRequest(slaveId, address, value);
|
||||
WriteRegisterResponse response = (WriteRegisterResponse)master.send(request);
|
||||
if (response.isException()) {
|
||||
logger.info("Write register failed: " + response.getExceptionMessage());
|
||||
} else {
|
||||
logger.info("Write register successful");
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to write register value '{}' to address '{}'", value, address, e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void writeRegistersRequest(ModbusMaster master, int slaveId, int address, short[] values) {
|
||||
try {
|
||||
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, address, values);
|
||||
WriteRegistersResponse response = (WriteRegistersResponse)master.send(request);
|
||||
if (response.isException()) {
|
||||
logger.info("Write registers failed: " + response.getExceptionMessage());
|
||||
} else {
|
||||
logger.info("Write registers successful");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to write registers value '{}' to address '{}'", values, address, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setCoilValue(ModbusMaster master, int slaveId, int address, int value) {
|
||||
// 写入单个线圈(从站ID, 地址, 布尔值)
|
||||
BaseLocator<Boolean> coilLocator = BaseLocator.coilStatus(slaveId, address);
|
||||
try {
|
||||
// 写入布尔值到线圈
|
||||
master.setValue(coilLocator, value);
|
||||
logger.info("set coil successful");
|
||||
} catch (ModbusTransportException | ErrorResponseException e) {
|
||||
logger.error("Failed to set coil value '{}' to address '{}'", value, address, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setRegisterValue(ModbusMaster master, int slaveId, int address, int value) {
|
||||
// 写入单个保持寄存器(从站, 地址, 16位整数)
|
||||
BaseLocator<Number> holdingLocator = BaseLocator.holdingRegister(slaveId, address, DataType.TWO_BYTE_INT_SIGNED);
|
||||
try {
|
||||
// 写入整数值到保持寄存器
|
||||
master.setValue(holdingLocator, value);
|
||||
logger.info("set register successful");
|
||||
} catch (ModbusTransportException | ErrorResponseException e) {
|
||||
logger.error("Failed to set register value '{}' to address '{}'", value, address, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ModbusMaster borrowMaster(DeviceConfig config) throws Exception {
|
||||
ModbusMaster master = connectionManager.borrowMaster(config);
|
||||
// 设置了Modbus通信的超时时间为5000毫秒(5秒)。当主设备与从设备通信时,若在5秒内未收到响应,则认为通信超时并抛出异常。这有助于避免长时间等待无响应的设备。
|
||||
master.setTimeout(readTimeout);
|
||||
master.setRetries(readRetries);
|
||||
return master;
|
||||
}
|
||||
|
||||
public Map<String, Object> readDataFromDevice(DeviceConfig config, ModbusMaster master) {
|
||||
Map<String, Object> deviceData = new HashMap<>();
|
||||
boolean hasError = false;
|
||||
try {
|
||||
BatchResults<String> results = readTagValues(master, config.getSlaveId(), config.getTags());
|
||||
for (TagConfig tag : config.getTags()) {
|
||||
if (Objects.equals(tag.getDataType(), "FOUR_BYTE_FLOAT_DBCA")){
|
||||
Object value = results.getValue(tag.getKey());
|
||||
value = convertValueToFloat(value);
|
||||
deviceData.put(tag.getKey(), value);
|
||||
}else {
|
||||
deviceData.put(tag.getKey(), results.getValue(tag.getKey()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed read from devices '{}'", config.getDeviceName(), e);
|
||||
hasError = true;
|
||||
}
|
||||
finally {
|
||||
if (master != null) {
|
||||
if (hasError) {
|
||||
// 发生异常时废弃连接,下次重新创建
|
||||
connectionManager.invalidateMaster(config, master);
|
||||
} else {
|
||||
// 正常时归还连接
|
||||
connectionManager.returnMaster(config, master);
|
||||
}
|
||||
}
|
||||
}
|
||||
return deviceData;
|
||||
}
|
||||
|
||||
private Object readTagValue(ModbusMaster master, int slaveId, TagConfig tag) throws Exception {
|
||||
Object value;
|
||||
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
|
||||
Map<String, Integer> DATA_LENGTH = ModBusType.LENGTH;
|
||||
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
|
||||
int address = 0;
|
||||
int addressLength = tag.getAddress().length();
|
||||
int exp = (int) Math.pow(10, addressLength-1);
|
||||
if (firstDigit != 0){
|
||||
int digit = Integer.parseInt(tag.getAddress());
|
||||
address = digit % (exp);
|
||||
}else {
|
||||
address = Integer.parseInt(tag.getAddress());
|
||||
}
|
||||
RegisterType registerType = type.get(firstDigit);
|
||||
int dataLength = DATA_LENGTH.get(tag.getDataType());
|
||||
// 增加配置校验和警告
|
||||
if ((Objects.equals(registerType, COIL) || Objects.equals(registerType, DISCRETE_INPUT))) {
|
||||
logger.warn("Configuration warning for tag '{}': ModBusType is {} but DataType is {}. DataType should be BOOLEAN. Proceeding with BOOLEAN read.",
|
||||
tag.getKey(), registerType, tag.getDataType());
|
||||
}
|
||||
try {
|
||||
switch (registerType) {
|
||||
case COIL: {
|
||||
|
||||
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, address);
|
||||
value = master.getValue(loc);
|
||||
logger.info("读取线圈{}成功: {}",tag.getAddress(), value);
|
||||
break;
|
||||
}
|
||||
case DISCRETE_INPUT: {
|
||||
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, address);
|
||||
value = master.getValue(loc);
|
||||
logger.info("读取输入{}成功: {}",tag.getAddress(), value);
|
||||
break;
|
||||
}
|
||||
case HOLDING_REGISTER: {
|
||||
if (dataLength == 28){
|
||||
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, 4);
|
||||
value = master.getValue(locator);
|
||||
//将 value 转换为二进制数据
|
||||
value = convertValueToFloat(value);
|
||||
}else{
|
||||
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, dataLength);
|
||||
value = master.getValue(locator);
|
||||
}
|
||||
logger.info("读取保持寄存器{}成功: {}",tag.getAddress(), value);
|
||||
break;
|
||||
}
|
||||
case INPUT_REGISTER: {
|
||||
BaseLocator<Number> locator = BaseLocator.inputRegister(slaveId, address, dataLength);
|
||||
value= master.getValue(locator);
|
||||
logger.info("读取输入寄存器{}成功: {}",tag.getAddress(), value);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logger.error("Unsupported register type: {}", registerType);
|
||||
value = null;
|
||||
break;
|
||||
}
|
||||
}catch (Exception e){
|
||||
logger.error("Failed to read tag '{}'", tag.getKey(), e);
|
||||
value = null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
private BatchResults<String> readTagValues(ModbusMaster master, int slaveId, List<TagConfig> tags) throws Exception {
|
||||
try {
|
||||
BatchResults<String> results = sendBatchRead(master, slaveId, tags);
|
||||
logBatchResults(tags, results);
|
||||
return results;
|
||||
} catch (Exception e){
|
||||
if (isTimeoutException(e)) {
|
||||
logger.warn("批量读取超时,尝试降级为单点读取,slaveId: {}", slaveId);
|
||||
BatchResults<String> fallback = new BatchResults<>();
|
||||
for (TagConfig tag : tags) {
|
||||
Object value = readTagValue(master, slaveId, tag);
|
||||
fallback.addResult(tag.getKey(), value);
|
||||
}
|
||||
logBatchResults(tags, fallback);
|
||||
return fallback;
|
||||
}
|
||||
logger.error("Failed to read master '{}'", slaveId, e);
|
||||
throw new Exception(e);
|
||||
}
|
||||
}
|
||||
|
||||
private BatchResults<String> sendBatchRead(ModbusMaster master, int slaveId, List<TagConfig> tags) throws Exception {
|
||||
BatchRead<String> batch = new BatchRead<>();
|
||||
tags.forEach(tag -> {
|
||||
Map<Integer, RegisterType> type = ModBusType.REGISTER_TYPE;
|
||||
Map<String, Integer> DATA_LENGTH = ModBusType.LENGTH;
|
||||
int firstDigit = Integer.parseInt(tag.getAddress().substring(0, 1));
|
||||
int address = 0;
|
||||
int addressLength = tag.getAddress().length();
|
||||
int exp = (int) Math.pow(10, addressLength - 1);
|
||||
if (firstDigit != 0) {
|
||||
int digit = Integer.parseInt(tag.getAddress());
|
||||
address = digit % (exp);
|
||||
} else {
|
||||
address = Integer.parseInt(tag.getAddress());
|
||||
}
|
||||
RegisterType registerType = type.get(firstDigit);
|
||||
int dataLength = DATA_LENGTH.get(tag.getDataType());
|
||||
switch (registerType) {
|
||||
case COIL: {
|
||||
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, address);
|
||||
batch.addLocator(tag.getKey(), loc);
|
||||
break;
|
||||
}
|
||||
case DISCRETE_INPUT: {
|
||||
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, address);
|
||||
batch.addLocator(tag.getKey(), loc);
|
||||
break;
|
||||
}
|
||||
case HOLDING_REGISTER: {
|
||||
if (dataLength == 28) {
|
||||
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, address, 4);
|
||||
batch.addLocator(tag.getKey(), locator);
|
||||
} else {
|
||||
BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, address, dataLength);
|
||||
batch.addLocator(tag.getKey(), loc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INPUT_REGISTER: {
|
||||
BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, address, dataLength);
|
||||
batch.addLocator(tag.getKey(), loc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return master.send(batch);
|
||||
}
|
||||
|
||||
private void logBatchResults(List<TagConfig> tags, BatchResults<String> results) {
|
||||
List<String> logInfoList = new ArrayList<>();
|
||||
for (TagConfig tag : tags) {
|
||||
StringBuilder logInfo = new StringBuilder();
|
||||
logInfo.append(tag.getAddress());
|
||||
if (tag.getBit() != null) {
|
||||
logInfo.append("(").append(tag.getBit()).append("):");
|
||||
} else {
|
||||
logInfo.append(":");
|
||||
}
|
||||
logInfo.append(results.getValue(tag.getKey()));
|
||||
logInfoList.add(logInfo.toString());
|
||||
}
|
||||
logger.info("批处理读取寄存器成功: {}", JSON.toJSONString(logInfoList));
|
||||
}
|
||||
|
||||
private boolean isTimeoutException(Throwable e) {
|
||||
Throwable current = e;
|
||||
while (current != null) {
|
||||
if (current instanceof com.serotonin.modbus4j.sero.messaging.TimeoutException) {
|
||||
return true;
|
||||
}
|
||||
current = current.getCause();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static float convertValueToFloat(Object value) {
|
||||
if (!(value instanceof Number)) {
|
||||
throw new IllegalArgumentException("Input must be a Number");
|
||||
}
|
||||
|
||||
int intValue = ((Number) value).intValue();
|
||||
String binaryData = Integer.toBinaryString(intValue);
|
||||
|
||||
// 补足32位二进制字符串
|
||||
if (binaryData.length() < 32) {
|
||||
binaryData = String.format("%32s", binaryData).replace(' ', '0');
|
||||
}
|
||||
|
||||
// 分割为4个字节并重组为 dcba 格式
|
||||
byte[] reorderedBytes = new byte[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int start = i * 8;
|
||||
String byteStr = binaryData.substring(start, start + 8);
|
||||
reorderedBytes[3 - i] = (byte) Integer.parseInt(byteStr, 2);
|
||||
}
|
||||
|
||||
// 使用 ByteBuffer 提高可读性和安全性
|
||||
return Float.intBitsToFloat(
|
||||
java.nio.ByteBuffer.wrap(reorderedBytes).getInt()
|
||||
);
|
||||
}
|
||||
|
||||
/** 转换寄存器地址 */
|
||||
public static int convertAddress(String address) {
|
||||
if (StringUtils.isBlank(address)) {
|
||||
return 0;
|
||||
}
|
||||
int firstDigit = Integer.parseInt(address.substring(0, 1));
|
||||
int addressLength = address.length();
|
||||
int exp = (int) Math.pow(10, addressLength-1);
|
||||
if (firstDigit != 0){
|
||||
int digit = Integer.parseInt(address);
|
||||
return digit % (exp);
|
||||
} else {
|
||||
return Integer.parseInt(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.xzzn.common.core.modbus;
|
||||
|
||||
|
||||
import com.serotonin.modbus4j.ModbusFactory;
|
||||
import com.serotonin.modbus4j.ModbusMaster;
|
||||
import com.serotonin.modbus4j.ip.IpParameters;
|
||||
|
||||
import org.apache.commons.pool2.BasePooledObjectFactory;
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
|
||||
public class PooledModbusMasterFactory extends BasePooledObjectFactory<ModbusMaster> {
|
||||
|
||||
private final String host;
|
||||
private final int port;
|
||||
|
||||
public PooledModbusMasterFactory(String host, int port) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModbusMaster create() throws Exception {
|
||||
IpParameters params = new IpParameters();
|
||||
params.setHost(this.host);
|
||||
params.setPort(this.port);
|
||||
params.setEncapsulated(false);
|
||||
|
||||
ModbusMaster master = new ModbusFactory().createTcpMaster(params, true);
|
||||
master.init();
|
||||
return master;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject<ModbusMaster> wrap(ModbusMaster master) {
|
||||
return new DefaultPooledObject<>(master);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(PooledObject<ModbusMaster> p) throws Exception {
|
||||
if (p.getObject() != null) {
|
||||
p.getObject().destroy();
|
||||
}
|
||||
super.destroyObject(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(PooledObject<ModbusMaster> p) {
|
||||
// testConnectivity() 方法在 modbus4j 中不存在。
|
||||
// 一个真正的验证需要执行一次读操作,但这需要 slaveId,而工厂是通用的,不知道 slaveId。
|
||||
// 因此,我们使用一个较弱的验证,只检查 master 对象是否已初始化。
|
||||
// 这无法检测到被远程设备断开的连接,错误将在实际读取操作中被捕获。
|
||||
if (p.getObject() == null) {
|
||||
return false;
|
||||
}
|
||||
return p.getObject().isInitialized();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.xzzn.common.core.modbus;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Modbus重试
|
||||
*
|
||||
*/
|
||||
public class RetryableModbusOperation {
|
||||
private static final Logger logger = LoggerFactory.getLogger(RetryableModbusOperation.class);
|
||||
|
||||
public interface ModbusOperation<T> {
|
||||
T execute() throws Exception;
|
||||
}
|
||||
|
||||
public static <T> T executeWithRetry(ModbusOperation<T> operation, int maxRetries) {
|
||||
int retryCount = 0;
|
||||
Exception lastException = null;
|
||||
|
||||
while (retryCount <= maxRetries) {
|
||||
try {
|
||||
return operation.execute();
|
||||
} catch (Exception e) {
|
||||
lastException = e;
|
||||
retryCount++;
|
||||
if (retryCount <= maxRetries) {
|
||||
logger.info("Operation failed, retrying... (Attempt {})", retryCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.error("Max retries ({}) reached. Last error: {}", maxRetries,
|
||||
lastException != null ? lastException.getMessage() : "Unknown");
|
||||
throw new RuntimeException("Operation failed after " + maxRetries + " retries", lastException);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
package com.xzzn.common.core.modbus.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "modbus")
|
||||
public class ModbusProperties {
|
||||
|
||||
private PoolConfig pool = new PoolConfig();
|
||||
private TimeoutConfig timeout = new TimeoutConfig();
|
||||
|
||||
public PoolConfig getPool() {
|
||||
return pool;
|
||||
}
|
||||
|
||||
public void setPool(PoolConfig pool) {
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
public TimeoutConfig getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(TimeoutConfig timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public static class PoolConfig {
|
||||
private int maxTotal = 20;
|
||||
private int maxIdle = 10;
|
||||
private int minIdle = 3;
|
||||
private long maxWait = 5000;
|
||||
private long timeBetweenEvictionRuns = 30000;
|
||||
private long minEvictableIdleTime = 60000;
|
||||
|
||||
public int getMaxTotal() {
|
||||
return maxTotal;
|
||||
}
|
||||
|
||||
public void setMaxTotal(int maxTotal) {
|
||||
this.maxTotal = maxTotal;
|
||||
}
|
||||
|
||||
public int getMaxIdle() {
|
||||
return maxIdle;
|
||||
}
|
||||
|
||||
public void setMaxIdle(int maxIdle) {
|
||||
this.maxIdle = maxIdle;
|
||||
}
|
||||
|
||||
public int getMinIdle() {
|
||||
return minIdle;
|
||||
}
|
||||
|
||||
public void setMinIdle(int minIdle) {
|
||||
this.minIdle = minIdle;
|
||||
}
|
||||
|
||||
public long getMaxWait() {
|
||||
return maxWait;
|
||||
}
|
||||
|
||||
public void setMaxWait(long maxWait) {
|
||||
this.maxWait = maxWait;
|
||||
}
|
||||
|
||||
public long getTimeBetweenEvictionRuns() {
|
||||
return timeBetweenEvictionRuns;
|
||||
}
|
||||
|
||||
public void setTimeBetweenEvictionRuns(long timeBetweenEvictionRuns) {
|
||||
this.timeBetweenEvictionRuns = timeBetweenEvictionRuns;
|
||||
}
|
||||
|
||||
public long getMinEvictableIdleTime() {
|
||||
return minEvictableIdleTime;
|
||||
}
|
||||
|
||||
public void setMinEvictableIdleTime(long minEvictableIdleTime) {
|
||||
this.minEvictableIdleTime = minEvictableIdleTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimeoutConfig {
|
||||
private int read = 10000;
|
||||
private int write = 5000;
|
||||
|
||||
public int getRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
public void setRead(int read) {
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
public int getWrite() {
|
||||
return write;
|
||||
}
|
||||
|
||||
public void setWrite(int write) {
|
||||
this.write = write;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package com.xzzn.common.core.modbus.domain;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class DeviceConfig {
|
||||
|
||||
|
||||
private String deviceNumber;
|
||||
private String deviceName;
|
||||
private String host;
|
||||
private int port;
|
||||
private long time;
|
||||
private int slaveId;
|
||||
private boolean enabled;
|
||||
private List<TagConfig> tags;
|
||||
private List<WriteTagConfig> writeTags;
|
||||
|
||||
public String getDeviceNumber() {
|
||||
return deviceNumber;
|
||||
}
|
||||
|
||||
public void setDeviceNumber(String deviceNumber) {
|
||||
this.deviceNumber = deviceNumber;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public int getSlaveId() {
|
||||
return slaveId;
|
||||
}
|
||||
|
||||
public void setSlaveId(int slaveId) {
|
||||
this.slaveId = slaveId;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public List<TagConfig> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(List<TagConfig> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public List<WriteTagConfig> getWriteTags() {
|
||||
return writeTags;
|
||||
}
|
||||
|
||||
public void setWriteTags(List<WriteTagConfig> writeTags) {
|
||||
this.writeTags = writeTags;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package com.xzzn.common.core.modbus.domain;
|
||||
|
||||
|
||||
|
||||
public class TagConfig {
|
||||
private String key;
|
||||
private String des;
|
||||
private String address;
|
||||
private String dataType;
|
||||
private float a;
|
||||
private float k;
|
||||
private float b;
|
||||
private Integer bit;
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getDes() {
|
||||
return des;
|
||||
}
|
||||
|
||||
public void setDes(String des) {
|
||||
this.des = des;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getDataType() {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
public void setDataType(String dataType) {
|
||||
this.dataType = dataType;
|
||||
}
|
||||
|
||||
public float getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(float a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public float getK() {
|
||||
return k;
|
||||
}
|
||||
|
||||
public void setK(float k) {
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
public float getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setB(float b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public Integer getBit() {
|
||||
return bit;
|
||||
}
|
||||
|
||||
public void setBit(Integer bit) {
|
||||
this.bit = bit;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.xzzn.common.core.modbus.domain;
|
||||
|
||||
public class WriteTagConfig {
|
||||
private Object value;
|
||||
private String address;
|
||||
private boolean isWrite;
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public boolean isWrite() {
|
||||
return isWrite;
|
||||
}
|
||||
|
||||
public void setWrite(boolean write) {
|
||||
isWrite = write;
|
||||
}
|
||||
}
|
||||
@ -7,10 +7,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.core.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@ -243,6 +241,18 @@ public class RedisCache
|
||||
return redisTemplate.opsForHash().multiGet(key, hKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Hash中的多条数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @return 是否成功
|
||||
*/
|
||||
public boolean deleteAllCacheMapValue(final String key, final Object[] hKey)
|
||||
{
|
||||
return redisTemplate.opsForHash().delete(key, hKey) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Hash中的某条数据
|
||||
*
|
||||
@ -255,6 +265,18 @@ public class RedisCache
|
||||
return redisTemplate.opsForHash().delete(key, hKey) > 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 批量往Hash中存入数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param value 值
|
||||
*/
|
||||
public <T> void setAllCacheMapValue(final String key, final Map<String, T> value)
|
||||
{
|
||||
redisTemplate.opsForHash().putAll(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的基本对象列表
|
||||
*
|
||||
@ -276,4 +298,78 @@ public class RedisCache
|
||||
{
|
||||
return redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量缓存
|
||||
* @param cacheMap
|
||||
*/
|
||||
public <T> void multiSet(final Map<String, String> cacheMap)
|
||||
{
|
||||
|
||||
redisTemplate.opsForValue().multiSet(cacheMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置键值对,并为每个键设置相同的过期时间
|
||||
* @param keyValueMap 键值对集合
|
||||
* @param timeout 过期时间
|
||||
* @param unit 时间单位
|
||||
*/
|
||||
public void multiSetWithExpire(Map<String, String> keyValueMap, long timeout, TimeUnit unit) {
|
||||
if (keyValueMap.isEmpty()) {
|
||||
return; // 空集合直接返回
|
||||
}
|
||||
|
||||
// 使用Redis管道批量执行命令
|
||||
redisTemplate.executePipelined(new SessionCallback<Object>() {
|
||||
@Override
|
||||
public Object execute(RedisOperations operations) throws DataAccessException {
|
||||
// 循环处理每个键值对
|
||||
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
|
||||
// 1. 设置键值对
|
||||
operations.opsForValue().set(key, value);
|
||||
// 2. 设置过期时间
|
||||
operations.expire(key, timeout, unit);
|
||||
}
|
||||
return null; // 管道操作无需返回值
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定站点的所有历史测试数据缓存(最精准)
|
||||
* @param testSiteId 测试数据所属的站点ID(如"test-site-001")
|
||||
*/
|
||||
public void deleteTestCacheBySite(String testSiteId) {
|
||||
System.out.println("开删除缓存redis:" + testSiteId + "<UNK>");
|
||||
if (testSiteId == null || testSiteId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 构造键前缀:测试站点ID开头的所有缓存键(无论粒度和时间)
|
||||
String pattern = testSiteId + "_*";
|
||||
deleteCacheByPattern(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据键前缀批量删除缓存(核心方法)
|
||||
* @param pattern 键匹配模式(支持Redis的通配符:*表示任意字符,?表示单个字符)
|
||||
*/
|
||||
public void deleteCacheByPattern(String pattern) {
|
||||
try {
|
||||
// 1. 查找所有匹配的键(注意:keys命令在大数据量时可能阻塞Redis,建议生产环境用scan)
|
||||
Set<String> keys = redisTemplate.keys(pattern);
|
||||
if (keys == null || keys.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 批量删除匹配的键
|
||||
long deleteCount = redisTemplate.delete(keys);
|
||||
System.out.println("删除缓存数据:" + deleteCount + "<UNK>");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,8 +7,10 @@ package com.xzzn.common.enums;
|
||||
*/
|
||||
public enum AmmeterCategory
|
||||
{
|
||||
CURRENT_COMB_ACTIVE("1", "当前组合有功电能"),
|
||||
CURRENT_COMB_REACTIVE("2", "当前组合无功电能"),
|
||||
CURRENT_FORWARD_ACTIVE("1", "当前正向有功电能"),
|
||||
CURRENT_FORWARD_REACTIVE("2", "当前正向无功电能"),
|
||||
CURRENT_REVERSE_ACTIVE("3", "当前反向有功电能"),
|
||||
CURRENT_REVERSE_REACTIVE("4", "当前反向无功电能"),
|
||||
A_POWER("3", "A相功率"),
|
||||
B_POWER("4", "B相功率"),
|
||||
C_POWER("5", "C相功率");
|
||||
|
||||
@ -7,7 +7,7 @@ package com.xzzn.common.enums;
|
||||
*/
|
||||
public enum CommunicationStatus
|
||||
{
|
||||
OK("0", "正常"), SUSPEND("1", "通信中断") ,EXCEPTION("1", "异常");
|
||||
OK("0", "正常"), SUSPEND("1", "通信中断") ,EXCEPTION("2", "异常");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
42
ems-common/src/main/java/com/xzzn/common/enums/CostType.java
Normal file
42
ems-common/src/main/java/com/xzzn/common/enums/CostType.java
Normal file
@ -0,0 +1,42 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
/**
|
||||
* 电量类型
|
||||
*/
|
||||
public enum CostType
|
||||
{
|
||||
PEAK("peak", "尖"),
|
||||
HIGH("high", "峰"),
|
||||
FLAT("flat", "平"),
|
||||
VALLEY("valley", "谷"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
CostType(String code, String info)
|
||||
{
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
public static CostType getEnumByCode(String code)
|
||||
{
|
||||
for (CostType costType : CostType.values()) {
|
||||
if (costType.code.equals(code)) {
|
||||
return costType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,10 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
import org.apache.xmlbeans.impl.common.NameUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* device-设备类别
|
||||
*
|
||||
@ -7,21 +12,27 @@ package com.xzzn.common.enums;
|
||||
*/
|
||||
public enum DeviceCategory
|
||||
{
|
||||
PCS("PCS", "PCS设备"),
|
||||
BRANCH("BRANCH", "PCS分支设备"),
|
||||
STACK("STACK", "电池堆"),
|
||||
CLUSTER("CLUSTER", "电池簇"),
|
||||
BATTERY("BATTERY", "单体电池"),
|
||||
AMMETER("AMMETER", "电表"),
|
||||
COOLING("COOLING", "冷液体");
|
||||
PCS("PCS", "PCS设备", null),
|
||||
BRANCH("BRANCH", "PCS分支设备", PCS),
|
||||
STACK("STACK", "电池堆", null),
|
||||
CLUSTER("CLUSTER", "电池簇", STACK),
|
||||
BATTERY("BATTERY", "单体电池", CLUSTER),
|
||||
AMMETER("AMMETER", "电表", null),
|
||||
COOLING("COOLING", "冷却", null),
|
||||
DH("DH", "动环", null),
|
||||
XF("XF", "消防", null),
|
||||
BATTERY_GROUP("BATTERY_GROUP", "电池组", null),
|
||||
EMS("EMS", "EMS设备", null),;
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
private final DeviceCategory parentCategory;
|
||||
|
||||
DeviceCategory(String code, String info)
|
||||
DeviceCategory(String code, String info, DeviceCategory parentCategory)
|
||||
{
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
this.parentCategory = parentCategory;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
@ -33,4 +44,43 @@ public enum DeviceCategory
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
public DeviceCategory getParentCategory()
|
||||
{
|
||||
return parentCategory;
|
||||
}
|
||||
// 缓存info与code的映射(优化查询效率)
|
||||
private static final Map<String, String> INFO_CODE_MAP = new HashMap<>();
|
||||
|
||||
// 静态块初始化缓存
|
||||
static {
|
||||
for (DeviceCategory category : DeviceCategory.values()) {
|
||||
INFO_CODE_MAP.put(category.info, category.code);
|
||||
}
|
||||
}
|
||||
|
||||
// 通过info获取code的方法
|
||||
public static String getCodeByInfo(String info) {
|
||||
return INFO_CODE_MAP.get(info); // 从缓存中直接获取,效率高
|
||||
}
|
||||
|
||||
// 通过code获取父类code
|
||||
// 根据字符串编码查找对应的枚举
|
||||
public static DeviceCategory fromCode(String code) {
|
||||
for (DeviceCategory category : values()) {
|
||||
if (category.code.equalsIgnoreCase(code)) { // 忽略大小写,增强兼容性
|
||||
return category;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getInfoByCode(String code) {
|
||||
for (DeviceCategory category : DeviceCategory.values()) {
|
||||
if (category.code.equals(code)) {
|
||||
return category.info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
/**
|
||||
* pcs-设备状态
|
||||
* device-通信状态
|
||||
*
|
||||
* @author xzzn
|
||||
*/
|
||||
public enum DeviceStatus
|
||||
public enum DeviceRunningStatus
|
||||
{
|
||||
ONLINE("0", "在线"), OFFLINE("1", "离线"), UNDER_REPAIR("2", "维修中");
|
||||
OFFLINE("0", "离线"),
|
||||
ONLINE("1", "在线");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
DeviceStatus(String code, String info)
|
||||
DeviceRunningStatus(String code, String info)
|
||||
{
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
@ -27,4 +28,5 @@ public enum DeviceStatus
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
import com.serotonin.modbus4j.code.DataType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class ModBusType {
|
||||
|
||||
|
||||
public static final Map<Integer, RegisterType> REGISTER_TYPE = new HashMap<Integer, RegisterType>();
|
||||
public static final Map<String, Integer> LENGTH = new HashMap<String, Integer>();
|
||||
|
||||
|
||||
|
||||
static {
|
||||
REGISTER_TYPE.put(0, RegisterType.COIL);
|
||||
REGISTER_TYPE.put(1, RegisterType.DISCRETE_INPUT);
|
||||
REGISTER_TYPE.put(4, RegisterType.HOLDING_REGISTER);
|
||||
REGISTER_TYPE.put(3, RegisterType.INPUT_REGISTER);
|
||||
|
||||
|
||||
LENGTH.put("BINARY",DataType.BINARY);
|
||||
LENGTH.put("TWO_BYTE_INT_UNSIGNED",DataType.TWO_BYTE_INT_UNSIGNED);
|
||||
LENGTH.put("TWO_BYTE_INT_SIGNED",DataType.TWO_BYTE_INT_SIGNED);
|
||||
LENGTH.put("TWO_BYTE_INT_UNSIGNED_SWAPPED",DataType.TWO_BYTE_INT_UNSIGNED_SWAPPED);
|
||||
LENGTH.put("TWO_BYTE_INT_SIGNED_SWAPPED",DataType.TWO_BYTE_INT_SIGNED_SWAPPED);
|
||||
LENGTH.put("FOUR_BYTE_INT_UNSIGNED",DataType.FOUR_BYTE_INT_UNSIGNED);
|
||||
LENGTH.put("FOUR_BYTE_INT_SIGNED",DataType.FOUR_BYTE_INT_SIGNED);
|
||||
LENGTH.put("FOUR_BYTE_INT_UNSIGNED_SWAPPED",DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED);
|
||||
LENGTH.put("FOUR_BYTE_INT_SIGNED_SWAPPED",DataType.FOUR_BYTE_INT_SIGNED_SWAPPED);
|
||||
LENGTH.put("FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED",DataType.FOUR_BYTE_INT_UNSIGNED_SWAPPED_SWAPPED);
|
||||
LENGTH.put("FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED",DataType.FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED);
|
||||
LENGTH.put("FOUR_BYTE_FLOAT",DataType.FOUR_BYTE_FLOAT);
|
||||
LENGTH.put("FOUR_BYTE_FLOAT_SWAPPED",DataType.FOUR_BYTE_FLOAT_SWAPPED);
|
||||
LENGTH.put("FOUR_BYTE_FLOAT_SWAPPED_INVERTED",DataType.FOUR_BYTE_FLOAT_SWAPPED_INVERTED);
|
||||
LENGTH.put("EIGHT_BYTE_INT_UNSIGNED",DataType.EIGHT_BYTE_INT_UNSIGNED);
|
||||
LENGTH.put("EIGHT_BYTE_INT_SIGNED",DataType.EIGHT_BYTE_INT_SIGNED);
|
||||
LENGTH.put("EIGHT_BYTE_INT_UNSIGNED_SWAPPED",DataType.EIGHT_BYTE_INT_UNSIGNED_SWAPPED);
|
||||
LENGTH.put("EIGHT_BYTE_INT_SIGNED_SWAPPED",DataType.EIGHT_BYTE_INT_SIGNED_SWAPPED);
|
||||
LENGTH.put("EIGHT_BYTE_FLOAT",DataType.EIGHT_BYTE_FLOAT);
|
||||
LENGTH.put("EIGHT_BYTE_FLOAT_SWAPPED",DataType.EIGHT_BYTE_FLOAT_SWAPPED);
|
||||
LENGTH.put("TWO_BYTE_BCD",DataType.TWO_BYTE_BCD);
|
||||
LENGTH.put("FOUR_BYTE_BCD",DataType.FOUR_BYTE_BCD);
|
||||
LENGTH.put("FOUR_BYTE_BCD_SWAPPED",DataType.FOUR_BYTE_BCD_SWAPPED);
|
||||
LENGTH.put("CHAR",DataType.CHAR);
|
||||
LENGTH.put("VARCHAR",DataType.VARCHAR);
|
||||
|
||||
LENGTH.put("FOUR_BYTE_FLOAT_DBCA",28);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设备点位类型
|
||||
*/
|
||||
public enum PointType
|
||||
{
|
||||
YES(1, "是"),
|
||||
NO(0, "否");
|
||||
|
||||
private final Integer code;
|
||||
private final String info;
|
||||
|
||||
private static final Map<Integer, String> CODE_NAME_MAP = new HashMap<>(PointType.values().length);
|
||||
|
||||
static {
|
||||
Arrays.stream(PointType.values()).forEach(record -> {
|
||||
CODE_NAME_MAP.put(record.code, record.info);
|
||||
});
|
||||
}
|
||||
|
||||
PointType(Integer code, String info)
|
||||
{
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getInfoByCode(Integer code) {
|
||||
return CODE_NAME_MAP.get(code);
|
||||
}
|
||||
|
||||
public Integer getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
/**
|
||||
* 告警保护方案状态
|
||||
*
|
||||
* @author xzzn
|
||||
*/
|
||||
public enum ProtPlanStatus
|
||||
{
|
||||
STOP(0L, "未启用"), RUNNING(1L, "已启用");
|
||||
|
||||
private final Long code;
|
||||
private final String info;
|
||||
|
||||
ProtPlanStatus(Long code, String info)
|
||||
{
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public Long getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
public enum RegisterType {
|
||||
COIL,
|
||||
DISCRETE_INPUT,
|
||||
HOLDING_REGISTER,
|
||||
INPUT_REGISTER
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public enum SiteDevice
|
||||
{
|
||||
/**
|
||||
* 电池堆
|
||||
*/
|
||||
BMSD,
|
||||
|
||||
/**
|
||||
* 电池簇
|
||||
*/
|
||||
BMSC,
|
||||
|
||||
/**
|
||||
* PCS设备
|
||||
*/
|
||||
PCS,
|
||||
|
||||
/**
|
||||
* 电表-总表
|
||||
*/
|
||||
LOAD,
|
||||
|
||||
/**
|
||||
* 电表-光伏电表
|
||||
*/
|
||||
METEGF,
|
||||
|
||||
/**
|
||||
* 电表-储能电表
|
||||
*/
|
||||
METE,
|
||||
|
||||
/**
|
||||
* 电表-储能电表
|
||||
*/
|
||||
METE0,
|
||||
|
||||
/**
|
||||
* 动环
|
||||
*/
|
||||
donghuan,
|
||||
|
||||
/**
|
||||
* 动环
|
||||
*/
|
||||
DH,
|
||||
|
||||
/**
|
||||
* 消防
|
||||
*/
|
||||
XF,
|
||||
|
||||
/**
|
||||
* 中水冷却
|
||||
*/
|
||||
ZSLQ,
|
||||
|
||||
/**
|
||||
* EMS
|
||||
*/
|
||||
EMS
|
||||
}
|
||||
31
ems-common/src/main/java/com/xzzn/common/enums/SiteEnum.java
Normal file
31
ems-common/src/main/java/com/xzzn/common/enums/SiteEnum.java
Normal file
@ -0,0 +1,31 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
/**
|
||||
* device-设备类型
|
||||
*
|
||||
* @author xzzn
|
||||
*/
|
||||
|
||||
public enum SiteEnum
|
||||
{
|
||||
DDS("021_DDS_01", "电动所内部"), FX("021_FXX_01", "奉贤西部污水处理厂");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
SiteEnum(String code, String info)
|
||||
{
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
}
|
||||
32
ems-common/src/main/java/com/xzzn/common/enums/SocLimit.java
Normal file
32
ems-common/src/main/java/com/xzzn/common/enums/SocLimit.java
Normal file
@ -0,0 +1,32 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
/**
|
||||
* SOC限制 (%) 1 = 开,0 = 关
|
||||
*
|
||||
* @author xzzn
|
||||
*/
|
||||
public enum SocLimit
|
||||
{
|
||||
OFF(0, "关"),
|
||||
ON(1, "开"),
|
||||
;
|
||||
|
||||
private final Integer code;
|
||||
private final String info;
|
||||
|
||||
SocLimit(Integer code, String info)
|
||||
{
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public Integer getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ package com.xzzn.common.enums;
|
||||
*/
|
||||
public enum StrategyStatus
|
||||
{
|
||||
NOT_ENABLED("0", "未启用"), RUNNING("1", "已运行"),SUSPENDED("2", "已暂停"), DISABLE("1", "禁用"),DELETE("2", "删除");
|
||||
NOT_ENABLED("0", "未启用"), RUNNING("1", "已运行"),SUSPENDED("2", "已暂停"), DISABLE("3", "禁用"),DELETE("4", "删除");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
/**
|
||||
* ticket - 工单状态
|
||||
*
|
||||
* @author xzzn
|
||||
*/
|
||||
public enum TicketStatus
|
||||
{
|
||||
WAITING("1", "待处理"), PROCESSING("2", "处理中"),DONE("3", "已处理");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
TicketStatus(String code, String info)
|
||||
{
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
/**
|
||||
* MQTT主题处理类型未知类型
|
||||
*/
|
||||
public enum TopicHandleType {
|
||||
DEVICE("DEVICE", "设备数据"),
|
||||
DEVICE_ALARM("DEVICE_ALARM", "设备告警数据"),
|
||||
STRATEGY("STRATEGY", "策略数据"),
|
||||
SYSTEM("SYSTEM", "系统数据"),
|
||||
;
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
TopicHandleType(String code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
// 根据名称查找枚举
|
||||
public static TopicHandleType getEnumByCode(String code) {
|
||||
for (TopicHandleType type : values()) {
|
||||
if (type.getName().equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
// 默认返回SYSTEM
|
||||
return SYSTEM;
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ package com.xzzn.common.enums;
|
||||
*/
|
||||
public enum WorkStatus
|
||||
{
|
||||
NORMAL("0", "正常"), ABNORMAL("1", "异常"), STOP("2", "停止");
|
||||
NORMAL("0", "运行"), STOP("1", "停机"), ABNORMAL("2", "故障");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
33
ems-common/src/main/java/com/xzzn/common/enums/YesOrNo.java
Normal file
33
ems-common/src/main/java/com/xzzn/common/enums/YesOrNo.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
public enum YesOrNo
|
||||
{
|
||||
YES(1, "是", "Y"),
|
||||
NO(0, "否", "N");
|
||||
|
||||
private final Integer code;
|
||||
private final String info;
|
||||
private final String value;
|
||||
|
||||
YesOrNo(Integer code, String info, String value)
|
||||
{
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Integer getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.xzzn.common.utils;
|
||||
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DataUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(DataUtils.class);
|
||||
|
||||
public static String getJSONFromFile(InputStream inputStream) {
|
||||
BufferedReader bufferReader = null;
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
try {
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
|
||||
bufferReader = new BufferedReader(inputStreamReader);
|
||||
stringBuffer = new StringBuffer();
|
||||
String lineData = null;
|
||||
while ((lineData = bufferReader.readLine()) != null) {
|
||||
stringBuffer.append(lineData);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("getJSONFromFile error", e);
|
||||
} finally {
|
||||
if (null != bufferReader) {
|
||||
try {
|
||||
bufferReader.close();
|
||||
inputStream.close();
|
||||
} catch (Exception e) {
|
||||
log.warn("getJSONFromFile error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
}
|
||||
@ -8,9 +8,14 @@ import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
@ -19,6 +24,8 @@ import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
*/
|
||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
||||
{
|
||||
private static final org.apache.commons.logging.Log log = LogFactory.getLog(DateUtils.class);
|
||||
|
||||
public static String YYYY = "yyyy";
|
||||
|
||||
public static String YYYY_MM = "yyyy-MM";
|
||||
@ -29,6 +36,13 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static String YYYYMMDD = "yyyyMMdd";
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_00 = "yyyy-MM-dd HH:mm:00";
|
||||
|
||||
private static final DateTimeFormatter DAY_INPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final DateTimeFormatter MIN_HOUR_INPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private static String[] parsePatterns = {
|
||||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
|
||||
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
|
||||
@ -145,6 +159,14 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
||||
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算相差分钟
|
||||
*/
|
||||
public static int differentMinutesByMillisecond(Date date1, Date date2)
|
||||
{
|
||||
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 60)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算时间差
|
||||
*
|
||||
@ -200,4 +222,247 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
||||
long date = calendar.get(Calendar.DAY_OF_MONTH); // 月份从0开始,所以要加1
|
||||
return date;
|
||||
}
|
||||
|
||||
// LocalDateTime 转 Date(带时区)
|
||||
public static Date convertToDate(LocalDateTime localDateTime) {
|
||||
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
|
||||
return Date.from(zonedDateTime.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取昨天的日期,格式为yyyy-MM-dd 23:59:59
|
||||
* @return 昨天的日期字符串
|
||||
*/
|
||||
public static String getYesterdayDate() {
|
||||
// 获取今天的日期
|
||||
LocalDate today = LocalDate.now();
|
||||
// 减去一天得到昨天
|
||||
LocalDate yesterday = today.minusDays(1);
|
||||
// 将日期转换为LocalDateTime,并设置时间为23:59:59
|
||||
LocalDateTime yesterdayEndTime = yesterday.atTime(23, 59, 59);
|
||||
// 定义日期格式化器
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS);
|
||||
// 格式化并返回
|
||||
return yesterdayEndTime.format(formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验传入日期是否今天
|
||||
* @param dataDate
|
||||
* @return
|
||||
*/
|
||||
public static boolean checkIsToday(String dataDate) {
|
||||
boolean flag = false;
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
int todayMonth = calendar.get(Calendar.MONTH) + 1;
|
||||
int todayDay = calendar.get(Calendar.DAY_OF_MONTH);
|
||||
if (StringUtils.isNotEmpty(dataDate)){
|
||||
String[] pcsDateArray = dataDate.split("-");
|
||||
if (todayMonth == Integer.parseInt(pcsDateArray[1]) &&
|
||||
todayDay == Integer.parseInt(pcsDateArray[2])) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将时间转换为月份(YYYY-MM)
|
||||
*/
|
||||
public static String formatMonth(LocalDateTime time) {
|
||||
return time.format(DateTimeFormatter.ofPattern("yyyy-MM"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加 LocalDateTime ==> String
|
||||
*/
|
||||
public static String convertToString(LocalDateTime time) {
|
||||
return time.format(DateTimeFormatter.ofPattern(YYYY_MM_DD_HH_MM_SS));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将输入时间调整到下一分钟的整点(秒数强制为00)
|
||||
* 例如:2026-09-03 18:34:49 -> 2026-09-03 18:35:00
|
||||
* @param timeStr 输入时间字符串(支持yyyy-MM-dd HH:mm:ss格式)
|
||||
* @return 调整后的时间字符串(yyyy-MM-dd HH:mm:00)
|
||||
* @throws ParseException 时间格式错误时抛出
|
||||
*/
|
||||
public static String adjustToNextMinute(String timeStr) throws ParseException {
|
||||
// 1. 解析原始时间(使用包含秒数的格式)
|
||||
SimpleDateFormat fullFormat = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS);
|
||||
Date originalDate = fullFormat.parse(timeStr);
|
||||
|
||||
// 2. 使用Calendar调整时间
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(originalDate);
|
||||
|
||||
// 3. 如果当前秒数大于0,自动进1分钟(否则保持当前分钟)
|
||||
if (cal.get(Calendar.SECOND) > 0) {
|
||||
cal.add(Calendar.MINUTE, 1); // 分钟+1
|
||||
}
|
||||
|
||||
// 4. 强制设置秒数和毫秒为0
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
// 5. 格式化为目标字符串
|
||||
return new SimpleDateFormat(YYYY_MM_DD_HH_MM_00).format(cal.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将"yyyy-MM-dd"字符串转换为Date类型的"yyyy-MM-dd 23:59:59"
|
||||
*/
|
||||
public static Date adjustToEndOfDay(String dateStr) {
|
||||
// 1. 解析字符串为LocalDate(仅日期)
|
||||
LocalDate localDate = LocalDate.parse(dateStr, DAY_INPUT_FORMATTER);
|
||||
|
||||
// 2. 补充时间为23:59:59,转换为LocalDateTime
|
||||
LocalDateTime localDateTime = localDate.atTime(23, 59, 59);
|
||||
|
||||
// 3. 转换为Date类型(兼容旧API)
|
||||
return Date.from(
|
||||
localDateTime.atZone(TimeZone.getDefault().toZoneId()) // 适配系统时区
|
||||
.toInstant()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 "yyyy-MM-dd HH:mm:ss" 字符串转换为Date类型的"yyyy-MM-dd HH:00:00"
|
||||
*/
|
||||
public static Date adjustToStartOfHour(String inputTime) {
|
||||
// 1. 解析输入字符串为LocalDateTime(包含日期和小时、分钟)
|
||||
LocalDateTime inputLdt = LocalDateTime.parse(inputTime, MIN_HOUR_INPUT_FORMATTER);
|
||||
|
||||
// 2. 调整分钟为00,秒为00(保留日期和小时不变)
|
||||
LocalDateTime adjustedLdt = inputLdt
|
||||
.withMinute(00) // 强制设为00分
|
||||
.withSecond(00); // 强制设为00秒
|
||||
|
||||
// 3. 转换为Date类型(适配旧API)
|
||||
return Date.from(
|
||||
adjustedLdt.atZone(TimeZone.getDefault().toZoneId()) // 适配系统时区
|
||||
.toInstant()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 "yyyy-MM-dd HH:mm:ss" 字符串转换为Date类型的"yyyy-MM-dd HH:mm:00"
|
||||
*/
|
||||
public static Date adjustToStartOfMinutes(String inputTime) {
|
||||
// 1. 解析输入字符串为LocalDateTime(包含日期和小时、分钟)
|
||||
LocalDateTime inputLdt = LocalDateTime.parse(inputTime, MIN_HOUR_INPUT_FORMATTER);
|
||||
|
||||
// 2. 调整分钟为00,秒为00(保留日期和小时不变)
|
||||
LocalDateTime adjustedLdt = inputLdt
|
||||
.withSecond(00); // 强制设为00秒
|
||||
|
||||
// 3. 转换为Date类型(适配旧API)
|
||||
return Date.from(
|
||||
adjustedLdt.atZone(TimeZone.getDefault().toZoneId()) // 适配系统时区
|
||||
.toInstant()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将时间转换为月份(YYYY-MM-DD HH:mm:00)
|
||||
*/
|
||||
public static Date timeConvertToDate(LocalDateTime time) {
|
||||
String dateStr = convertToString(time);
|
||||
return dateTime(YYYY_MM_DD_HH_MM_SS,dateStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加 Date ==> LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime toLocalDateTime(Date date)
|
||||
{
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1. Date → LocalDateTime(保留原始时间)
|
||||
LocalDateTime dateTime = date.toInstant()
|
||||
.atZone(TimeZone.getDefault().toZoneId()) // 关联时区
|
||||
.toLocalDateTime();
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
// 时间戳转时间
|
||||
public static Date convertUpdateTime(Long updateTime) {
|
||||
if (updateTime == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 兼容10位(秒级)和13位(毫秒级)
|
||||
int length = String.valueOf(updateTime).length();
|
||||
if (length == 10) { // 10位秒级 -> 转换为毫秒级
|
||||
updateTime *= 1000;
|
||||
} else if (length != 13) { // 既不是10位也不是13位,视为非法
|
||||
System.err.println("时间戳格式错误,必须是10位(秒)或13位(毫秒)");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. 转换为Date
|
||||
return new Date(updateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取昨天的日期,格式为yyyy-MM-dd
|
||||
* @return 昨天的日期字符串
|
||||
*/
|
||||
public static String getYesterdayDayString() {
|
||||
// 获取今天的日期
|
||||
LocalDate today = LocalDate.now();
|
||||
// 减去一天得到昨天
|
||||
LocalDate yesterday = today.minusDays(1);
|
||||
// 定义日期格式化器
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YYYY_MM_DD);
|
||||
// 格式化并返回
|
||||
return yesterday.format(formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日期的开始时间
|
||||
* @param dateString 格式为yyyy-MM-dd的日期字符串
|
||||
* @return 日期的开始时间字符串
|
||||
*/
|
||||
public static String getDayBeginString(String dateString) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
try {
|
||||
// 解析日期字符串
|
||||
LocalDate date = LocalDate.parse(dateString, formatter);
|
||||
|
||||
// 创建时间段的开始时间 00:00:00
|
||||
LocalDateTime startTime = date.atTime(LocalTime.MIN);
|
||||
|
||||
return convertToString(startTime);
|
||||
} catch (DateTimeParseException e) {
|
||||
log.info("Error parsing date: " + e.getMessage());
|
||||
}
|
||||
|
||||
return dateString + " 00:00:00";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日期的结束时间
|
||||
* @param dateString 格式为yyyy-MM-dd的日期字符串
|
||||
* @return 日期的结束时间字符串
|
||||
*/
|
||||
public static String getDayEndString(String dateString) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
try {
|
||||
// 解析日期字符串
|
||||
LocalDate date = LocalDate.parse(dateString, formatter);
|
||||
|
||||
// 创建时间段的结束时间 23:59:59
|
||||
LocalDateTime endTime = date.atTime(LocalTime.MAX);
|
||||
|
||||
return convertToString(endTime);
|
||||
} catch (DateTimeParseException e) {
|
||||
log.info("Error parsing date: " + e.getMessage());
|
||||
}
|
||||
|
||||
return dateString + " 23:59:59";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
44
ems-common/src/main/java/com/xzzn/common/utils/MapUtils.java
Normal file
44
ems-common/src/main/java/com/xzzn/common/utils/MapUtils.java
Normal file
@ -0,0 +1,44 @@
|
||||
package com.xzzn.common.utils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class MapUtils {
|
||||
/**
|
||||
* 从Map中获取值并转为Integer(适配tinyint字段)
|
||||
* @param map 数据源Map
|
||||
* @param key 字段名
|
||||
* @return 转换后的Integer,默认返回0(可根据业务调整默认值)
|
||||
*/
|
||||
public static Integer getInteger(Map<String, Object> map, String key) {
|
||||
// 1. 处理Map为null或key不存在的情况
|
||||
if (map == null || !map.containsKey(key)) {
|
||||
return 0;
|
||||
}
|
||||
// 2. 获取原始值
|
||||
Object value = map.get(key);
|
||||
if (value == null) {
|
||||
return 0;
|
||||
}
|
||||
// 3. 转换为Integer(处理常见类型)
|
||||
if (value instanceof Integer) {
|
||||
return (Integer) value;
|
||||
} else if (value instanceof Long) {
|
||||
// 若Map中存的是Long(如JSON解析时数字默认转为Long)
|
||||
return ((Long) value).intValue();
|
||||
} else if (value instanceof String) {
|
||||
// 若值是字符串类型(如"1")
|
||||
try {
|
||||
return Integer.parseInt((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
// 字符串无法转数字,返回默认值
|
||||
return 0;
|
||||
}
|
||||
} else if (value instanceof Boolean) {
|
||||
// 特殊情况:布尔值转整数(true→1,false→0)
|
||||
return (Boolean) value ? 1 : 0;
|
||||
} else {
|
||||
// 其他不支持的类型,返回默认值
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
package com.xzzn.common.utils;
|
||||
|
||||
import com.xzzn.common.constant.Constants;
|
||||
import com.xzzn.common.core.text.StrFormatter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@ -7,9 +10,8 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import com.xzzn.common.constant.Constants;
|
||||
import com.xzzn.common.core.text.StrFormatter;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
@ -743,6 +745,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||
}
|
||||
|
||||
public static String getString(Object s){
|
||||
if (s == null) return null;
|
||||
String result;
|
||||
try {
|
||||
result = String.valueOf(s);
|
||||
@ -751,4 +754,17 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 补全三位数字(返回字符串)
|
||||
public static String fillThreeDigits(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return "000";
|
||||
}
|
||||
try {
|
||||
int num = Integer.parseInt(value.trim());
|
||||
return String.format("%03d", num);
|
||||
} catch (NumberFormatException e) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,11 +50,7 @@
|
||||
<groupId>org.eclipse.paho</groupId>
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
</dependency>
|
||||
<!-- 轮询 -->
|
||||
<dependency>
|
||||
<groupId>net.wimpi</groupId>
|
||||
<artifactId>j2mod</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
|
||||
@ -0,0 +1,97 @@
|
||||
package com.xzzn.framework.aspectj;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsDeviceChangeLog;
|
||||
import com.xzzn.ems.domain.MqttSyncLog;
|
||||
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||
import com.xzzn.ems.mapper.MqttSyncLogMapper;
|
||||
import com.xzzn.framework.web.service.MqttPublisher;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 设备运行状态变更日志同步
|
||||
* 本地 - 云端
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class DeviceChangeLogAspect {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DeviceChangeLogAspect.class);
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final String MQTT_TOPIC = "DEVICE_CHANGE_LOG_UP";
|
||||
private static final String TABLE_NAME = "ems_device_change_log";
|
||||
@Autowired
|
||||
private MqttSyncLogMapper mqttSyncLogMapper;
|
||||
@Autowired
|
||||
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||
|
||||
// 定义切点:拦截策略相关表的Mapper方法
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsDeviceChangeLogMapper.insertEmsDeviceChangeLog(..)) && args(insertEntity)) ")
|
||||
public void insertPointCut(EmsDeviceChangeLog insertEntity) {
|
||||
logger.info("【新增设备运行状态变更日志】DeviceChangeLogAspect 实例化");
|
||||
}
|
||||
|
||||
// 方法执行成功后发布同步消息
|
||||
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
|
||||
public void afterInsert(JoinPoint joinPoint, EmsDeviceChangeLog insertEntity, Integer result) {
|
||||
logger.info("【新增设备运行状态变更日志下发数据切面进入成功】");
|
||||
if (result == 0 || insertEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String operateType = "INSERT";
|
||||
String siteId = insertEntity.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType,siteId);
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
String content = objectMapper.writeValueAsString(insertEntity);
|
||||
message.setContent(content);
|
||||
|
||||
// mqtt同步到云端
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
|
||||
// 构建同步信息
|
||||
private MqttSyncLog createMessageObject(String operateType, String siteId) {
|
||||
MqttSyncLog message = new MqttSyncLog();
|
||||
message.setSyncId(UUID.randomUUID().toString());
|
||||
message.setOperateType(operateType);
|
||||
message.setTableName(TABLE_NAME);
|
||||
message.setCreateTime(new Date());
|
||||
message.setTopic(MQTT_TOPIC);
|
||||
message.setStatus("SUCCESS");
|
||||
message.setSyncObject(siteId);
|
||||
message.setTarget("CLOUD");
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package com.xzzn.framework.aspectj;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsAlarmRecords;
|
||||
import com.xzzn.ems.domain.MqttSyncLog;
|
||||
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||
import com.xzzn.ems.mapper.MqttSyncLogMapper;
|
||||
import com.xzzn.framework.web.service.MqttPublisher;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 本地保护告警信息同步云端
|
||||
* 本地 - 云端
|
||||
* @author xzzn
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class FaultPlanAlarmAspect
|
||||
{
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FaultPlanAlarmAspect.class);
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@Autowired
|
||||
private MqttSyncLogMapper mqttSyncLogMapper;
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
@Autowired
|
||||
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||
|
||||
private static final String MQTT_TOPIC = "FAULT_ALARM_RECORD_UP";
|
||||
private static final String TABLE_NAME = "ems_alarm_records";
|
||||
// 切入点:拦截所有添加了@SyncAfterInsert注解的方法
|
||||
@Pointcut("@annotation(com.xzzn.common.annotation.SyncAfterInsert)")
|
||||
public void syncInsertPointcut() {}
|
||||
|
||||
// 方法执行成功后同步数据
|
||||
@AfterReturning(pointcut = "syncInsertPointcut()", returning = "result")
|
||||
public void afterInsert(JoinPoint joinPoint, Integer result) {
|
||||
logger.info("【新增保护策略告警数据切面进入成功】");
|
||||
if (result == 0) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取参数中的EmsAlarmRecords对象
|
||||
Object[] args = joinPoint.getArgs();
|
||||
EmsAlarmRecords alarmRecords = null;
|
||||
for (Object arg : args) {
|
||||
if (arg instanceof EmsAlarmRecords) {
|
||||
alarmRecords = (EmsAlarmRecords) arg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (alarmRecords == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject("INSERT", alarmRecords.getSiteId());
|
||||
try {
|
||||
// 同步内容
|
||||
String content = objectMapper.writeValueAsString(alarmRecords);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题-同步到云端
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
|
||||
// 构建同步信息
|
||||
private MqttSyncLog createMessageObject(String operateType, String siteId) {
|
||||
MqttSyncLog message = new MqttSyncLog();
|
||||
message.setSyncId(UUID.randomUUID().toString());
|
||||
message.setOperateType(operateType);
|
||||
message.setTableName(TABLE_NAME);
|
||||
message.setCreateTime(new Date());
|
||||
message.setTopic(MQTT_TOPIC);
|
||||
message.setStatus("SUCCESS");
|
||||
message.setSyncObject(siteId);
|
||||
message.setTarget("CLOUD");
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package com.xzzn.framework.aspectj;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsFaultIssueLog;
|
||||
import com.xzzn.ems.domain.MqttSyncLog;
|
||||
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||
import com.xzzn.ems.mapper.MqttSyncLogMapper;
|
||||
import com.xzzn.framework.web.service.MqttPublisher;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 保护告警方案下发日志同步
|
||||
* 本地 - 云端
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class FaultPlanIssueAspect {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FaultPlanIssueAspect.class);
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final String MQTT_TOPIC = "FAULT_PLAN_ISSUE_UP";
|
||||
private static final String TABLE_NAME = "ems_fault_issue_log";
|
||||
@Autowired
|
||||
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||
@Autowired
|
||||
private MqttSyncLogMapper mqttSyncLogMapper;
|
||||
|
||||
// 定义切点:拦截策略相关表的Mapper方法
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsFaultIssueLogMapper.insertEmsFaultIssueLog(..)) && args(insertEntity)) ")
|
||||
public void insertPointCut(EmsFaultIssueLog insertEntity) {
|
||||
logger.info("【新增保护告警策略下发数据】FaultPlanIssueAspect 实例化");
|
||||
}
|
||||
|
||||
// 方法执行成功后发布同步消息
|
||||
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
|
||||
public void afterInsert(JoinPoint joinPoint, EmsFaultIssueLog insertEntity, Integer result) {
|
||||
logger.info("【新增保护告警策略下发数据切面进入成功】");
|
||||
if (result == 0 || insertEntity == null) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String operateType = "INSERT";
|
||||
String siteId = insertEntity.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType,siteId);
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
String content = objectMapper.writeValueAsString(insertEntity);
|
||||
message.setContent(content);
|
||||
|
||||
// mqtt同步到云端
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
|
||||
// 构建同步信息
|
||||
private MqttSyncLog createMessageObject(String operateType, String siteId) {
|
||||
MqttSyncLog message = new MqttSyncLog();
|
||||
message.setSyncId(UUID.randomUUID().toString());
|
||||
message.setOperateType(operateType);
|
||||
message.setTableName(TABLE_NAME);
|
||||
message.setCreateTime(new Date());
|
||||
message.setTopic(MQTT_TOPIC);
|
||||
message.setStatus("SUCCESS");
|
||||
message.setSyncObject(siteId);
|
||||
message.setTarget("CLOUD");
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,277 @@
|
||||
package com.xzzn.framework.aspectj;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
|
||||
import com.xzzn.ems.domain.MqttSyncLog;
|
||||
import com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper;
|
||||
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||
import com.xzzn.ems.mapper.MqttSyncLogMapper;
|
||||
import com.xzzn.framework.web.service.MqttPublisher;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 设备保护告警方案同步
|
||||
* 云端 - 本地
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class FaultProtPlanAspect {
|
||||
private static final Log logger = LogFactory.getLog(FaultProtPlanAspect.class);
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final String MQTT_TOPIC = "FAULT_PROTECTION_PLAN_UP";
|
||||
private static final String TABLE_NAME = "ems_fault_protection_plan";
|
||||
@Autowired
|
||||
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||
@Autowired
|
||||
private MqttSyncLogMapper mqttSyncLogMapper;
|
||||
@Autowired
|
||||
private EmsFaultProtectionPlanMapper emsFaultProtectionPlanMapper;
|
||||
|
||||
// 用ThreadLocal暂存删除前的对象
|
||||
private ThreadLocal<EmsFaultProtectionPlan> beforeDeleteThreadLocal = new ThreadLocal<>();
|
||||
@Before("execution(* com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper.deleteEmsFaultProtectionPlanByIds(..)) && args(ids)")
|
||||
public void beforeDelete(JoinPoint joinPoint, Long[] ids) {
|
||||
// 获取删除的id
|
||||
Object[] args = joinPoint.getArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
return;
|
||||
}
|
||||
Long[] id = (Long[]) args[0];
|
||||
|
||||
// 查询删除前的数据
|
||||
EmsFaultProtectionPlan faultInfo = emsFaultProtectionPlanMapper.selectEmsFaultProtectionPlanById(id[0]);
|
||||
beforeDeleteThreadLocal.set(faultInfo); // 暂存
|
||||
}
|
||||
|
||||
// 定义切点:拦截策略相关表的Mapper方法
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper.insertEmsFaultProtectionPlan(..)) && args(insertEntity)) ")
|
||||
public void insertPointCut(EmsFaultProtectionPlan insertEntity) {
|
||||
logger.info("【新增设备保护告警】FaultProtPlanAspect 实例化");
|
||||
}
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(..)) && args(updateEntity)) ")
|
||||
public void updatePointCut(EmsFaultProtectionPlan updateEntity) {
|
||||
logger.info("【更新设备保护告警】FaultProtPlanAspect 实例化");
|
||||
}
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper.deleteEmsFaultProtectionPlanByIds(..)) && args(ids)) ")
|
||||
public void deletePointCut(Long[] ids) {
|
||||
logger.info("【删除设备保护告警】FaultProtPlanAspect 实例化");
|
||||
}
|
||||
|
||||
// 方法执行成功后发布同步消息
|
||||
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
|
||||
public void afterInsert(JoinPoint joinPoint, EmsFaultProtectionPlan insertEntity, Integer result) {
|
||||
logger.info("【新增设备保护告警切面进入成功】");
|
||||
if (result == 0 || insertEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
String siteId = insertEntity.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType,siteId);
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
String content = convertEntityToJson(insertEntity);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
@AfterReturning(pointcut = "updatePointCut(updateEntity)", returning = "result")
|
||||
public void afterUpdate(JoinPoint joinPoint, EmsFaultProtectionPlan updateEntity, Integer result) {
|
||||
logger.info("【更新设备保护告警切面进入成功】");
|
||||
if (result == 0 || updateEntity == null) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
String siteId = updateEntity.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType,siteId);
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
String content = convertEntityToJson(updateEntity);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
@AfterReturning(pointcut = "deletePointCut(id)", returning = "result")
|
||||
public void afterDelete(JoinPoint joinPoint, Long[] id, Integer result) {
|
||||
logger.info("【删除设备保护告警切面进入成功】");
|
||||
if (result == 0 || id == null) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
|
||||
// 从ThreadLocal中获取删除前的对象
|
||||
EmsFaultProtectionPlan faultInfo = beforeDeleteThreadLocal.get();
|
||||
if (faultInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType,faultInfo.getSiteId());
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
String content = convertEntityToJson(faultInfo);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
|
||||
// 构建同步信息
|
||||
private MqttSyncLog createMessageObject(String operateType, String siteId) {
|
||||
MqttSyncLog message = new MqttSyncLog();
|
||||
message.setSyncId(UUID.randomUUID().toString());
|
||||
message.setOperateType(operateType);
|
||||
message.setTableName(TABLE_NAME);
|
||||
message.setCreateTime(new Date());
|
||||
message.setTopic(MQTT_TOPIC);
|
||||
message.setStatus("SUCCESS");
|
||||
message.setSyncObject("CLOUD");
|
||||
message.setTarget(siteId);
|
||||
return message;
|
||||
}
|
||||
|
||||
// 从方法名判断操作类型(示例:insert→INSERT,update→UPDATE,delete→DELETE)
|
||||
private String getOperateType(String methodName) {
|
||||
if (methodName.startsWith("insert")) return "INSERT";
|
||||
if (methodName.startsWith("stop")) return "STOP";
|
||||
if (methodName.startsWith("update") || methodName.startsWith("stop")) return "UPDATE";
|
||||
if (methodName.startsWith("delete")) return "DELETE";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
// 从方法参数提取数据(示例:若参数是实体类,转成Map)
|
||||
private Map<String, Object> extractDataFromParams(Object[] args) {
|
||||
// 实际需反射获取实体类的字段和值(如id、name等)
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
if (args == null || args.length == 0) {
|
||||
return data;
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
if (arg == null) {
|
||||
continue; // 跳过null参数
|
||||
}
|
||||
|
||||
// 处理基本类型/包装类/字符串(直接作为值存入,key为"param0"、"param1"等)
|
||||
if (isBasicType(arg.getClass())) {
|
||||
String key = "param" + i; // 基本类型参数用"param0"、"param1"作为key
|
||||
data.put(key, arg);
|
||||
} else {
|
||||
Map<String, Object> beanMap = beanToMap(arg);
|
||||
data.putAll(beanMap); // 合并实体类的字段到结果Map
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为基本类型或包装类或字符串
|
||||
*/
|
||||
private boolean isBasicType(Class<?> clazz) {
|
||||
return clazz.isPrimitive() // 基本类型(int、long、boolean等)
|
||||
|| clazz == String.class // 字符串
|
||||
|| Number.class.isAssignableFrom(clazz) // 数字包装类(Integer、Long等)
|
||||
|| clazz == Boolean.class; // 布尔包装类
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体类转换为Map(字段名为key,字段值为value)
|
||||
*/
|
||||
private Map<String, Object> beanToMap(Object bean) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
if (bean == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
// 方式1:使用BeanMap(简洁高效)
|
||||
BeanMap beanMap = BeanMap.create(bean);
|
||||
for (Object key : beanMap.keySet()) {
|
||||
map.put(key.toString(), beanMap.get(key));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
// 在方法中转换
|
||||
public String convertEntityToJson(EmsFaultProtectionPlan insertEntity) throws Exception {
|
||||
if (insertEntity == null) {
|
||||
return null; // 空对象返回空JSON
|
||||
}
|
||||
|
||||
// 将实体类转换为JSON字符串
|
||||
return objectMapper.writeValueAsString(insertEntity);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,272 @@
|
||||
package com.xzzn.framework.aspectj;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsStrategyRunning;
|
||||
import com.xzzn.ems.domain.MqttSyncLog;
|
||||
import com.xzzn.ems.domain.vo.StrategyRunningVo;
|
||||
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
|
||||
import com.xzzn.ems.mapper.MqttSyncLogMapper;
|
||||
import com.xzzn.framework.web.service.MqttPublisher;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 策略运行切面同步
|
||||
* 云端 - 本地
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class StrategyRunningSyncAspect {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(StrategyRunningSyncAspect.class);
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final String MQTT_TOPIC = "EMS_STRATEGY_UP";
|
||||
private static final String TABLE_NAME = "ems_strategy_running";
|
||||
@Autowired
|
||||
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||
@Autowired
|
||||
private MqttSyncLogMapper mqttSyncLogMapper;
|
||||
@Autowired
|
||||
private EmsStrategyRunningMapper emsStrategyRunningMapper;
|
||||
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyRunningMapper.stopEmsStrategyRunning(..)) && args(id)) ")
|
||||
public void stopPointCut(Long id) {
|
||||
logger.info("【停止策略切面】StrategyAspect 被实例化");
|
||||
}
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyRunningMapper.insertEmsStrategyRunning(..)) && args(insertEntity)) ")
|
||||
public void insertPointCut(EmsStrategyRunning insertEntity) {
|
||||
logger.info("【新增策略切面】StrategyAspect 被实例化");
|
||||
}
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyRunningMapper.updateEmsStrategyRunning(..)) && args(updateEntity)) ")
|
||||
public void updatePointCut(EmsStrategyRunning updateEntity) {
|
||||
logger.info("【更新策略切面】StrategyAspect 被实例化");
|
||||
}
|
||||
|
||||
// 方法执行成功后发布同步消息
|
||||
@AfterReturning(pointcut = "updatePointCut(updateEntity)", returning = "result")
|
||||
public void afterUpdate(JoinPoint joinPoint, EmsStrategyRunning updateEntity, Integer result) {
|
||||
logger.info("【更新策略切面进入成功】");
|
||||
if (result == 0 || updateEntity == null) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
String siteId = updateEntity.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType,siteId);
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
List<StrategyRunningVo> runningVos = emsStrategyRunningMapper.getRunningList(siteId);
|
||||
if (runningVos == null || runningVos.size() == 0) {
|
||||
return;
|
||||
}
|
||||
StrategyRunningVo runningVo = runningVos.get(0);
|
||||
String content = objectMapper.writeValueAsString(runningVo);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
|
||||
public void afterInsert(JoinPoint joinPoint, EmsStrategyRunning insertEntity, Integer result) {
|
||||
logger.info("【新增策略切面进入成功】");
|
||||
if (result == 0 || insertEntity == null) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
String siteId = insertEntity.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType, siteId);
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
List<StrategyRunningVo> runningVos = emsStrategyRunningMapper.getRunningList(siteId);
|
||||
if (runningVos == null || runningVos.size() == 0) {
|
||||
return;
|
||||
}
|
||||
StrategyRunningVo runningVo = runningVos.get(0);
|
||||
String content = objectMapper.writeValueAsString(runningVo);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "stopPointCut(id)", returning = "result")
|
||||
public void afterStop(JoinPoint joinPoint, Long id, Integer result) {
|
||||
logger.info("【停止策略切面进入成功】");
|
||||
if (result == 0 || id == null) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
EmsStrategyRunning emsStrategyRunning = emsStrategyRunningMapper.selectEmsStrategyRunningById(id);
|
||||
String siteId = emsStrategyRunning==null ? null : emsStrategyRunning.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType, siteId);
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
Map<String, Object> idMap = new HashMap<>();
|
||||
idMap.put("id", id); // 手动将参数值映射到"id"字段
|
||||
String content = JSON.toJSONString(idMap);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
|
||||
// 构建同步信息
|
||||
private MqttSyncLog createMessageObject(String operateType, String siteId) {
|
||||
MqttSyncLog message = new MqttSyncLog();
|
||||
message.setSyncId(UUID.randomUUID().toString());
|
||||
message.setOperateType(operateType);
|
||||
message.setTableName(TABLE_NAME);
|
||||
message.setCreateTime(new Date());
|
||||
message.setTopic(MQTT_TOPIC);
|
||||
message.setStatus("SUCCESS");
|
||||
message.setSyncObject("CLOUD");
|
||||
message.setTarget(siteId);
|
||||
return message;
|
||||
}
|
||||
// 从方法名判断操作类型(示例:insert→INSERT,update→UPDATE,delete→DELETE)
|
||||
private String getOperateType(String methodName) {
|
||||
if (methodName.startsWith("insert")) return "INSERT";
|
||||
if (methodName.startsWith("stop")) return "STOP";
|
||||
if (methodName.startsWith("update") || methodName.startsWith("stop")) return "UPDATE";
|
||||
if (methodName.startsWith("delete")) return "DELETE";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
// 从方法参数提取数据(示例:若参数是实体类,转成Map)
|
||||
private Map<String, Object> extractDataFromParams(Object[] args) {
|
||||
// 实际需反射获取实体类的字段和值(如id、name等)
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
if (args == null || args.length == 0) {
|
||||
return data;
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
if (arg == null) {
|
||||
continue; // 跳过null参数
|
||||
}
|
||||
|
||||
// 处理基本类型/包装类/字符串(直接作为值存入,key为"param0"、"param1"等)
|
||||
if (isBasicType(arg.getClass())) {
|
||||
String key = "param" + i; // 基本类型参数用"param0"、"param1"作为key
|
||||
data.put(key, arg);
|
||||
} else {
|
||||
Map<String, Object> beanMap = beanToMap(arg);
|
||||
data.putAll(beanMap); // 合并实体类的字段到结果Map
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为基本类型或包装类或字符串
|
||||
*/
|
||||
private boolean isBasicType(Class<?> clazz) {
|
||||
return clazz.isPrimitive() // 基本类型(int、long、boolean等)
|
||||
|| clazz == String.class // 字符串
|
||||
|| Number.class.isAssignableFrom(clazz) // 数字包装类(Integer、Long等)
|
||||
|| clazz == Boolean.class; // 布尔包装类
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体类转换为Map(字段名为key,字段值为value)
|
||||
*/
|
||||
private Map<String, Object> beanToMap(Object bean) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
if (bean == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
// 方式1:使用BeanMap(简洁高效)
|
||||
BeanMap beanMap = BeanMap.create(bean);
|
||||
for (Object key : beanMap.keySet()) {
|
||||
map.put(key.toString(), beanMap.get(key));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
// 在方法中转换
|
||||
public String convertEntityToJson(EmsStrategyRunning insertEntity) throws Exception {
|
||||
if (insertEntity == null) {
|
||||
return null; // 空对象返回空JSON
|
||||
}
|
||||
|
||||
// 将实体类转换为JSON字符串
|
||||
return objectMapper.writeValueAsString(insertEntity);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,261 @@
|
||||
package com.xzzn.framework.aspectj;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.common.utils.bean.BeanUtils;
|
||||
import com.xzzn.ems.domain.EmsStrategy;
|
||||
import com.xzzn.ems.domain.EmsStrategyRunning;
|
||||
import com.xzzn.ems.domain.EmsStrategyTemp;
|
||||
import com.xzzn.ems.domain.MqttSyncLog;
|
||||
import com.xzzn.ems.domain.vo.SyncStrategyTempVo;
|
||||
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
|
||||
import com.xzzn.ems.mapper.MqttSyncLogMapper;
|
||||
import com.xzzn.framework.web.service.MqttPublisher;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 策略模板数据同步
|
||||
* 云端 - 本地
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class StrategyTempSyncAspect {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(StrategyTempSyncAspect.class);
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final String MQTT_TOPIC = "EMS_STRATEGY_UP";
|
||||
private static final String TABLE_NAME = "ems_strategy_temp";
|
||||
@Autowired
|
||||
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||
@Autowired
|
||||
private MqttSyncLogMapper mqttSyncLogMapper;
|
||||
@Autowired
|
||||
private EmsStrategyMapper emsStrategyMapper;
|
||||
@Autowired
|
||||
private EmsStrategyTempMapper emsStrategyTempMapper;
|
||||
|
||||
// 用ThreadLocal暂存删除前的对象
|
||||
private ThreadLocal<EmsStrategyTemp> beforeDeleteThreadLocal = new ThreadLocal<>();
|
||||
@Before("execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.deleteTempByTempId(..)) && args(templateId)")
|
||||
public void beforeDelete(JoinPoint joinPoint, String templateId) {
|
||||
// 查询删除前的数据-仅存一获取siteId
|
||||
List<EmsStrategyTemp> tempList = emsStrategyTempMapper.selectStrategyTempByTempId(templateId);
|
||||
if (tempList != null && tempList.size() > 0) {
|
||||
beforeDeleteThreadLocal.set(tempList.get(0)); // 暂存
|
||||
}
|
||||
}
|
||||
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.deleteTempByTempId(..)) && args(templateId)) ")
|
||||
public void deletePointCut(String templateId) {
|
||||
logger.info("【删除策略模版切面】StrategyTempSyncAspect 被实例化");
|
||||
}
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.insertEmsStrategyTemp(..)) && args(insertEntity)) ")
|
||||
public void insertPointCut(EmsStrategyTemp insertEntity) {
|
||||
logger.info("【新增策略模版切面】StrategyTempSyncAspect 被实例化");
|
||||
}
|
||||
|
||||
// 方法执行成功后发布同步消息
|
||||
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
|
||||
public void afterInsert(JoinPoint joinPoint, EmsStrategyTemp insertEntity, Integer result) {
|
||||
logger.info("【新增策略切面进入成功】");
|
||||
if (result == 0 || insertEntity == null) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
String siteId = insertEntity.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType, siteId);
|
||||
|
||||
try {
|
||||
// 校验策略id是否存在
|
||||
Long strategyId = insertEntity.getStrategyId();
|
||||
if (strategyId == null) {
|
||||
return;
|
||||
}
|
||||
// 数据转换
|
||||
SyncStrategyTempVo tempVo = convertEntity(insertEntity);
|
||||
String content = JSON.toJSONString(tempVo);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
private SyncStrategyTempVo convertEntity(EmsStrategyTemp insertEntity) {
|
||||
SyncStrategyTempVo tempVo = new SyncStrategyTempVo();
|
||||
BeanUtils.copyProperties(insertEntity, tempVo);
|
||||
EmsStrategy strategy = emsStrategyMapper.selectEmsStrategyById(insertEntity.getStrategyId());
|
||||
if (strategy != null) {
|
||||
tempVo.setStrategyName(strategy.getStrategyName());
|
||||
tempVo.setStrategyType(strategy.getStrategyType());
|
||||
}
|
||||
return tempVo;
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "deletePointCut(templateId)", returning = "result")
|
||||
public void afterDelete(JoinPoint joinPoint, String templateId, Integer result) {
|
||||
logger.info("【删除策略模版切面进入成功】");
|
||||
if (result == 0 || StringUtils.isEmpty(templateId)) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
// 从ThreadLocal中获取删除前的对象
|
||||
EmsStrategyTemp strategyTemp = beforeDeleteThreadLocal.get();
|
||||
String siteId = "";
|
||||
if (strategyTemp != null) {
|
||||
siteId = strategyTemp.getSiteId();
|
||||
}
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType, siteId);
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
Map<String, Object> idMap = new HashMap<>();
|
||||
idMap.put("templateId", templateId); // 手动将参数值映射到"id"字段
|
||||
String content = JSON.toJSONString(idMap);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
|
||||
// 构建同步信息
|
||||
private MqttSyncLog createMessageObject(String operateType, String siteId) {
|
||||
MqttSyncLog message = new MqttSyncLog();
|
||||
message.setSyncId(UUID.randomUUID().toString());
|
||||
message.setOperateType(operateType);
|
||||
message.setTableName(TABLE_NAME);
|
||||
message.setCreateTime(new Date());
|
||||
message.setTopic(MQTT_TOPIC);
|
||||
message.setStatus("SUCCESS");
|
||||
message.setSyncObject("CLOUD");
|
||||
message.setTarget(siteId);
|
||||
return message;
|
||||
}
|
||||
// 从方法名判断操作类型(示例:insert→INSERT,update→UPDATE,delete→DELETE)
|
||||
private String getOperateType(String methodName) {
|
||||
if (methodName.startsWith("insert")) return "INSERT";
|
||||
if (methodName.startsWith("stop")) return "STOP";
|
||||
if (methodName.startsWith("update") || methodName.startsWith("stop")) return "UPDATE";
|
||||
if (methodName.startsWith("delete")) return "DELETE";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
// 从方法参数提取数据(示例:若参数是实体类,转成Map)
|
||||
private Map<String, Object> extractDataFromParams(Object[] args) {
|
||||
// 实际需反射获取实体类的字段和值(如id、name等)
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
if (args == null || args.length == 0) {
|
||||
return data;
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
if (arg == null) {
|
||||
continue; // 跳过null参数
|
||||
}
|
||||
|
||||
// 处理基本类型/包装类/字符串(直接作为值存入,key为"param0"、"param1"等)
|
||||
if (isBasicType(arg.getClass())) {
|
||||
String key = "param" + i; // 基本类型参数用"param0"、"param1"作为key
|
||||
data.put(key, arg);
|
||||
} else {
|
||||
Map<String, Object> beanMap = beanToMap(arg);
|
||||
data.putAll(beanMap); // 合并实体类的字段到结果Map
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为基本类型或包装类或字符串
|
||||
*/
|
||||
private boolean isBasicType(Class<?> clazz) {
|
||||
return clazz.isPrimitive() // 基本类型(int、long、boolean等)
|
||||
|| clazz == String.class // 字符串
|
||||
|| Number.class.isAssignableFrom(clazz) // 数字包装类(Integer、Long等)
|
||||
|| clazz == Boolean.class; // 布尔包装类
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体类转换为Map(字段名为key,字段值为value)
|
||||
*/
|
||||
private Map<String, Object> beanToMap(Object bean) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
if (bean == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
// 方式1:使用BeanMap(简洁高效)
|
||||
BeanMap beanMap = BeanMap.create(bean);
|
||||
for (Object key : beanMap.keySet()) {
|
||||
map.put(key.toString(), beanMap.get(key));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
// 在方法中转换
|
||||
public String convertEntityToJson(EmsStrategyRunning insertEntity) throws Exception {
|
||||
if (insertEntity == null) {
|
||||
return null; // 空对象返回空JSON
|
||||
}
|
||||
|
||||
// 将实体类转换为JSON字符串
|
||||
return objectMapper.writeValueAsString(insertEntity);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,241 @@
|
||||
package com.xzzn.framework.aspectj;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.common.utils.bean.BeanUtils;
|
||||
import com.xzzn.ems.domain.EmsStrategy;
|
||||
import com.xzzn.ems.domain.EmsStrategyRunning;
|
||||
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
|
||||
import com.xzzn.ems.domain.MqttSyncLog;
|
||||
import com.xzzn.ems.domain.vo.SyncStrategyTimeConfigVo;
|
||||
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
|
||||
import com.xzzn.ems.mapper.MqttSyncLogMapper;
|
||||
import com.xzzn.framework.web.service.MqttPublisher;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 策略时间配置同步
|
||||
* 云端 - 本地
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class StrategyTimeConfigSyncAspect {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(StrategyTimeConfigSyncAspect.class);
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private static final String MQTT_TOPIC = "EMS_STRATEGY_UP";
|
||||
private static final String TABLE_NAME = "ems_strategy_temp";
|
||||
@Autowired
|
||||
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||
@Autowired
|
||||
private MqttSyncLogMapper mqttSyncLogMapper;
|
||||
@Autowired
|
||||
private EmsStrategyMapper emsStrategyMapper;
|
||||
@Autowired
|
||||
private EmsStrategyTempMapper emsStrategyTempMapper;
|
||||
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTimeConfigMapper.insertEmsStrategyTimeConfig(..)) && args(insertEntity)) ")
|
||||
public void insertPointCut(EmsStrategyTimeConfig insertEntity) {
|
||||
logger.info("【新增策略模版时间配置切面】StrategyTimeConfigSyncAspect 被实例化");
|
||||
}
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTimeConfigMapper.updateEmsStrategyTimeConfig(..)) && args(updateEntity)) ")
|
||||
public void updatePointCut(EmsStrategyTimeConfig updateEntity) {
|
||||
logger.info("【更新策略模版时间配置切面】StrategyTimeConfigSyncAspect 被实例化");
|
||||
}
|
||||
|
||||
// 方法执行成功后发布同步消息
|
||||
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
|
||||
public void afterInsert(JoinPoint joinPoint, EmsStrategyTimeConfig insertEntity, Integer result) {
|
||||
logger.info("【新增策略模版时间切面进入成功】");
|
||||
if (result == 0 || insertEntity == null) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
String siteId = insertEntity.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType, siteId);
|
||||
|
||||
try {
|
||||
// 校验策略id是否存在
|
||||
Long strategyId = insertEntity.getStrategyId();
|
||||
if (strategyId == null) {
|
||||
return;
|
||||
}
|
||||
// 数据转换
|
||||
SyncStrategyTimeConfigVo timeConfigVo = convertEntity(insertEntity);
|
||||
String content = objectMapper.writeValueAsString(timeConfigVo);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
private SyncStrategyTimeConfigVo convertEntity(EmsStrategyTimeConfig insertEntity) {
|
||||
SyncStrategyTimeConfigVo timeConfigVo = new SyncStrategyTimeConfigVo();
|
||||
BeanUtils.copyProperties(insertEntity, timeConfigVo);
|
||||
EmsStrategy strategy = emsStrategyMapper.selectEmsStrategyById(insertEntity.getStrategyId());
|
||||
if (strategy != null) {
|
||||
timeConfigVo.setStrategyName(strategy.getStrategyName());
|
||||
timeConfigVo.setStrategyType(strategy.getStrategyType());
|
||||
}
|
||||
return timeConfigVo;
|
||||
}
|
||||
|
||||
@AfterReturning(pointcut = "updatePointCut(updateEntity)", returning = "result")
|
||||
public void afterUpdate(JoinPoint joinPoint, EmsStrategyTimeConfig updateEntity, Integer result) {
|
||||
logger.info("【删除策略模版切面进入成功】");
|
||||
if (result == 0 || updateEntity == null) {
|
||||
return;
|
||||
}
|
||||
// 校验是否配置监听topic-监听则不发布
|
||||
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||
if (!StringUtils.isEmpty(topic)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||
String methodName = joinPoint.getSignature().getName();
|
||||
String operateType = getOperateType(methodName);
|
||||
String siteId = updateEntity.getSiteId();
|
||||
|
||||
// 构建日志同步消息
|
||||
MqttSyncLog message = createMessageObject(operateType, siteId);
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
SyncStrategyTimeConfigVo timeConfigVo = convertEntity(updateEntity);
|
||||
String content = objectMapper.writeValueAsString(timeConfigVo);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||
} catch (Exception e) {
|
||||
message.setStatus("FAIL");
|
||||
message.setErrorMsg(e.getMessage());
|
||||
}
|
||||
// 存储同步信息
|
||||
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||
}
|
||||
|
||||
// 构建同步信息
|
||||
private MqttSyncLog createMessageObject(String operateType, String siteId) {
|
||||
MqttSyncLog message = new MqttSyncLog();
|
||||
message.setSyncId(UUID.randomUUID().toString());
|
||||
message.setOperateType(operateType);
|
||||
message.setTableName(TABLE_NAME);
|
||||
message.setCreateTime(new Date());
|
||||
message.setTopic(MQTT_TOPIC);
|
||||
message.setStatus("SUCCESS");
|
||||
message.setSyncObject("CLOUD");
|
||||
message.setTarget(siteId);
|
||||
return message;
|
||||
}
|
||||
// 从方法名判断操作类型(示例:insert→INSERT,update→UPDATE,delete→DELETE)
|
||||
private String getOperateType(String methodName) {
|
||||
if (methodName.startsWith("insert")) return "INSERT";
|
||||
if (methodName.startsWith("stop")) return "STOP";
|
||||
if (methodName.startsWith("update") || methodName.startsWith("stop")) return "UPDATE";
|
||||
if (methodName.startsWith("delete")) return "DELETE";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
// 从方法参数提取数据(示例:若参数是实体类,转成Map)
|
||||
private Map<String, Object> extractDataFromParams(Object[] args) {
|
||||
// 实际需反射获取实体类的字段和值(如id、name等)
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
if (args == null || args.length == 0) {
|
||||
return data;
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
if (arg == null) {
|
||||
continue; // 跳过null参数
|
||||
}
|
||||
|
||||
// 处理基本类型/包装类/字符串(直接作为值存入,key为"param0"、"param1"等)
|
||||
if (isBasicType(arg.getClass())) {
|
||||
String key = "param" + i; // 基本类型参数用"param0"、"param1"作为key
|
||||
data.put(key, arg);
|
||||
} else {
|
||||
Map<String, Object> beanMap = beanToMap(arg);
|
||||
data.putAll(beanMap); // 合并实体类的字段到结果Map
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为基本类型或包装类或字符串
|
||||
*/
|
||||
private boolean isBasicType(Class<?> clazz) {
|
||||
return clazz.isPrimitive() // 基本类型(int、long、boolean等)
|
||||
|| clazz == String.class // 字符串
|
||||
|| Number.class.isAssignableFrom(clazz) // 数字包装类(Integer、Long等)
|
||||
|| clazz == Boolean.class; // 布尔包装类
|
||||
}
|
||||
|
||||
/**
|
||||
* 将实体类转换为Map(字段名为key,字段值为value)
|
||||
*/
|
||||
private Map<String, Object> beanToMap(Object bean) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
if (bean == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
// 方式1:使用BeanMap(简洁高效)
|
||||
BeanMap beanMap = BeanMap.create(bean);
|
||||
for (Object key : beanMap.keySet()) {
|
||||
map.put(key.toString(), beanMap.get(key));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
// 在方法中转换
|
||||
public String convertEntityToJson(EmsStrategyRunning insertEntity) throws Exception {
|
||||
if (insertEntity == null) {
|
||||
return null; // 空对象返回空JSON
|
||||
}
|
||||
|
||||
// 将实体类转换为JSON字符串
|
||||
return objectMapper.writeValueAsString(insertEntity);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -13,8 +13,9 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
* @author xzzn
|
||||
*/
|
||||
@Configuration
|
||||
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||
// proxyTargetClass = true表示使用cglib代理,false表示jdk动态代理
|
||||
// exposeProxy = true表示通过aop框架暴露该代理对象,AopContext能够访问
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
|
||||
// 指定要扫描的Mapper类的包的路径
|
||||
@MapperScan("com.xzzn.**.mapper")
|
||||
public class ApplicationConfig
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
package com.xzzn.framework.config;
|
||||
|
||||
import com.xzzn.framework.manager.ModbusConnectionManager;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class ModbusConfig {
|
||||
|
||||
@Value("${modbus.pool.max-total:20}")
|
||||
private int maxTotal;
|
||||
|
||||
@Value("${modbus.pool.max-idle:10}")
|
||||
private int maxIdle;
|
||||
|
||||
@Value("${modbus.pool.min-idle:3}")
|
||||
private int minIdle;
|
||||
|
||||
@Value("${modbus.pool.max-wait:3000}")
|
||||
private long maxWaitMillis;
|
||||
|
||||
@Value("${modbus.pool.time-between-eviction-runs:30000}")
|
||||
private long timeBetweenEvictionRunsMillis;
|
||||
|
||||
@Value("${modbus.pool.min-evictable-idle-time:60000}")
|
||||
private long minEvictableIdleTimeMillis;
|
||||
|
||||
public ModbusConnectionManager modbusConnectionManager() {
|
||||
ModbusConnectionManager manager = new ModbusConnectionManager();
|
||||
manager.setMaxTotal(maxTotal);
|
||||
manager.setMaxIdle(maxIdle);
|
||||
manager.setMinIdle(minIdle);
|
||||
manager.setMaxWaitMillis(maxWaitMillis);
|
||||
manager.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
|
||||
manager.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
@ -1,254 +0,0 @@
|
||||
package com.xzzn.framework.manager;
|
||||
|
||||
import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
|
||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
||||
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class ModbusConnectionManager implements ApplicationRunner {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ModbusConnectionManager.class);
|
||||
|
||||
private final Map<Integer, ModbusConnectionWrapper> connectionPool = new ConcurrentHashMap<>();
|
||||
// 连接池配置参数
|
||||
private int maxTotal = 20;
|
||||
private int maxIdle = 10;
|
||||
private int minIdle = 3;
|
||||
private long maxWaitMillis = 3000;
|
||||
private long timeBetweenEvictionRunsMillis = 30000;
|
||||
private long minEvictableIdleTimeMillis = 60000;
|
||||
|
||||
private ScheduledExecutorService scheduler;
|
||||
@Autowired
|
||||
private EmsDevicesSettingMapper deviceRepo;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
// 启动心跳检测线程
|
||||
scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
scheduler.scheduleAtFixedRate(this::heartbeatCheck, 1, 5, TimeUnit.MINUTES);
|
||||
logger.info("Modbus连接管理器已初始化");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接(带自动创建和缓存)
|
||||
*/
|
||||
|
||||
public ModbusConnectionWrapper getConnection(EmsDevicesSetting device) throws Exception {
|
||||
return connectionPool.compute(Math.toIntExact(device.getId()), (id, wrapper) -> {
|
||||
try {
|
||||
if (wrapper == null || !wrapper.isActive()) {
|
||||
if (connectionPool.size() >= maxTotal) {
|
||||
evictIdleConnection();
|
||||
}
|
||||
logger.info("创建新连接: {}", device);
|
||||
return new ModbusConnectionWrapper(createRawConnection(device));
|
||||
}
|
||||
wrapper.updateLastAccess();
|
||||
return wrapper;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("连接创建失败: " + device.getId(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建原始Modbus连接
|
||||
*/
|
||||
private TCPMasterConnection createRawConnection(EmsDevicesSetting device) throws Exception {
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByName("192.168.80.100");
|
||||
TCPMasterConnection connection = new TCPMasterConnection(addr);
|
||||
connection.setPort(502);
|
||||
connection.setTimeout(5000);
|
||||
connection.connect();
|
||||
return connection;
|
||||
} catch (Exception e) {
|
||||
logger.error("创建Modbus连接失败: {}", device, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 心跳检测
|
||||
*/
|
||||
private void heartbeatCheck() {
|
||||
logger.info("开始监控Modbus连接池状态,当前连接数: {}", connectionPool.size());
|
||||
|
||||
// 步骤1:获取所有活跃设备列表(与轮询逻辑共用同一批设备)
|
||||
List<EmsDevicesSetting> activeDevices = null;
|
||||
if (activeDevices == null || activeDevices.isEmpty()) {
|
||||
logger.warn("无活跃设备,心跳检测仅清理无效连接");
|
||||
}
|
||||
|
||||
// 步骤2:清理无效连接(遍历连接池,移除已失效的连接)
|
||||
List<Integer> invalidDeviceIds = new ArrayList<>();
|
||||
connectionPool.forEach((deviceId, wrapper) -> {
|
||||
try {
|
||||
if (!wrapper.isActive()) {
|
||||
logger.info("连接{}已失效,移除连接", deviceId);
|
||||
invalidDeviceIds.add(deviceId);
|
||||
wrapper.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("心跳检测异常: {}", deviceId, e);
|
||||
}
|
||||
});
|
||||
|
||||
// 批量移除无效连接(避免边遍历边修改)
|
||||
invalidDeviceIds.forEach(connectionPool::remove);
|
||||
logger.debug("移除无效连接后,连接池大小: {}", connectionPool.size());
|
||||
|
||||
// 步骤3:补充关键设备的连接(优先保障活跃设备的连接存在)
|
||||
if (!activeDevices.isEmpty()) {
|
||||
// 3.1 先为所有活跃设备预加载连接(确保需要轮询的设备有连接)
|
||||
preloadCriticalConnection(activeDevices);
|
||||
|
||||
// 3.2 若连接数仍不足minIdle,补充额外连接(可选,避免连接池过小)
|
||||
int currentSize = connectionPool.size();
|
||||
if (currentSize < minIdle) {
|
||||
logger.info("连接数{}不足最小空闲数{},补充额外连接", currentSize, minIdle);
|
||||
// 从活跃设备中选未创建连接的设备补充(避免重复创建)
|
||||
List<EmsDevicesSetting> needMoreDevices = activeDevices.stream()
|
||||
.filter(device -> !connectionPool.containsKey(Math.toIntExact(device.getId())))
|
||||
.limit(minIdle - currentSize) // 只补充差额
|
||||
.collect(Collectors.toList());
|
||||
|
||||
preloadCriticalConnection(needMoreDevices); // 复用预加载方法
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载关键连接
|
||||
*/
|
||||
|
||||
private void preloadCriticalConnection(List<EmsDevicesSetting> devices) {
|
||||
// 简化示例,不实现具体逻辑
|
||||
logger.info("预加载连接: 连接池当前大小={}, 最小空闲={}", connectionPool.size(), minIdle);
|
||||
devices.forEach(device -> {
|
||||
try {
|
||||
Integer deviceId = Math.toIntExact(device.getId());
|
||||
if (!connectionPool.containsKey(deviceId)) {
|
||||
getConnection(device); // 复用已有创建逻辑
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("预加载设备{}连接失败", device.getId(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除最久未使用的空闲连接
|
||||
*/
|
||||
private void evictIdleConnection() {
|
||||
if (connectionPool.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModbusConnectionWrapper oldestWrapper = null;
|
||||
long oldestAccessTime = Long.MAX_VALUE;
|
||||
|
||||
for (ModbusConnectionWrapper wrapper : connectionPool.values()) {
|
||||
if (wrapper.isActive() && wrapper.getLastAccessTime() < oldestAccessTime) {
|
||||
oldestAccessTime = wrapper.getLastAccessTime();
|
||||
oldestWrapper = wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldestWrapper != null) {
|
||||
logger.info("移除空闲连接: {}", oldestWrapper.getConnection());
|
||||
connectionPool.values().remove(oldestWrapper);
|
||||
oldestWrapper.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 移除指定设备连接
|
||||
public void removeConnection(Integer deviceId) {
|
||||
ModbusConnectionWrapper wrapper = connectionPool.remove(deviceId);
|
||||
if (wrapper != null) {
|
||||
wrapper.close(); // 双重保障,确保连接关闭
|
||||
logger.info("连接池主动移除设备{}的连接", deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否应该移除空连接池
|
||||
*/
|
||||
private boolean shouldRemoveEmptyPool(GenericObjectPool<?> pool) {
|
||||
// 可根据配置或逻辑决定是否移除空连接池
|
||||
// 这里简单实现为当连接池数量超过最大值时移除
|
||||
return connectionPool.size() > maxTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
*/
|
||||
private void closeConnection(TCPMasterConnection connection) {
|
||||
try {
|
||||
if (connection != null && connection.isConnected()) {
|
||||
connection.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("关闭Modbus连接失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 容器销毁时关闭线程池
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
if (scheduler != null) {
|
||||
scheduler.shutdown();
|
||||
try {
|
||||
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
scheduler.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
scheduler.shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public void setMaxTotal(int maxTotal) {
|
||||
this.maxTotal = maxTotal;
|
||||
}
|
||||
|
||||
public void setMaxIdle(int maxIdle) {
|
||||
this.maxIdle = maxIdle;
|
||||
}
|
||||
|
||||
public void setMinIdle(int minIdle) {
|
||||
this.minIdle = minIdle;
|
||||
}
|
||||
|
||||
public void setMaxWaitMillis(long maxWaitMillis) {
|
||||
this.maxWaitMillis = maxWaitMillis;
|
||||
}
|
||||
|
||||
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
|
||||
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
|
||||
}
|
||||
|
||||
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
|
||||
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
|
||||
}
|
||||
}
|
||||
@ -1,81 +0,0 @@
|
||||
package com.xzzn.framework.manager;
|
||||
|
||||
import com.ghgande.j2mod.modbus.net.SerialConnection;
|
||||
import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ModbusConnectionWrapper {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ModbusConnectionWrapper.class);
|
||||
|
||||
private static final AtomicInteger COUNTER = new AtomicInteger(0);
|
||||
|
||||
private final Object connection;
|
||||
private final int connectionId;
|
||||
private volatile long lastAccessTime;
|
||||
private volatile boolean active = true;
|
||||
|
||||
public ModbusConnectionWrapper(Object connection) {
|
||||
this.connection = connection;
|
||||
this.connectionId = COUNTER.incrementAndGet();
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
logger.info("创建连接包装: {}", this);
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
if (!active) return false;
|
||||
|
||||
try {
|
||||
// 检查连接是否物理上有效
|
||||
if (connection instanceof TCPMasterConnection) {
|
||||
return ((TCPMasterConnection) connection).isConnected();
|
||||
} else if (connection instanceof SerialConnection) {
|
||||
return ((SerialConnection) connection).isOpen();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("连接状态检查失败: {}", connectionId, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 默认检查空闲时间
|
||||
return System.currentTimeMillis() - lastAccessTime < 300000; // 5分钟
|
||||
}
|
||||
|
||||
public void updateLastAccess() {
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Object getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
logger.info("关闭连接: {}", this);
|
||||
if (connection instanceof TCPMasterConnection) {
|
||||
((TCPMasterConnection) connection).close();
|
||||
} else if (connection instanceof SerialConnection) {
|
||||
((SerialConnection) connection).close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("关闭连接失败: {}", connectionId, e);
|
||||
} finally {
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
public long getLastAccessTime() {
|
||||
return lastAccessTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ModbusConnectionWrapper{" +
|
||||
"connectionId=" + connectionId +
|
||||
", active=" + active +
|
||||
", lastAccessTime=" + lastAccessTime +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,39 @@
|
||||
package com.xzzn.framework.manager;
|
||||
|
||||
import com.xzzn.ems.service.IEmsAlarmRecordsService;
|
||||
import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@Component
|
||||
public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle, MqttCallback {
|
||||
public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle, MqttCallbackExtended {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MqttLifecycleManager.class);
|
||||
private static final long RECONNECT_DELAY_SECONDS = 5;
|
||||
|
||||
private final MqttConnectOptions connectOptions;
|
||||
private final IEmsAlarmRecordsService iEmsAlarmRecordsService;
|
||||
private final ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setName("mqtt-reconnect");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
private final AtomicBoolean reconnectScheduled = new AtomicBoolean(false);
|
||||
private volatile ScheduledFuture<?> reconnectFuture;
|
||||
private MqttClient mqttClient;
|
||||
private volatile boolean running = false;
|
||||
|
||||
@ -23,8 +41,9 @@ public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle,
|
||||
private final ConcurrentHashMap<String, SubscriptionInfo> subscriptions = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
public MqttLifecycleManager(MqttConnectOptions connectOptions) {
|
||||
public MqttLifecycleManager(MqttConnectOptions connectOptions, IEmsAlarmRecordsService iEmsAlarmRecordsService) {
|
||||
this.connectOptions = connectOptions;
|
||||
this.iEmsAlarmRecordsService = iEmsAlarmRecordsService;
|
||||
}
|
||||
|
||||
// Spring Boot 启动完成后执行
|
||||
@ -38,7 +57,9 @@ public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle,
|
||||
if (running) return;
|
||||
|
||||
try {
|
||||
String clientId = connectOptions.getUserName() + "-" + System.currentTimeMillis();
|
||||
String prefix = connectOptions.getUserName() == null || connectOptions.getUserName().isEmpty()
|
||||
? "mqtt-client" : connectOptions.getUserName();
|
||||
String clientId = prefix + "-" + System.currentTimeMillis();
|
||||
mqttClient = new MqttClient(
|
||||
connectOptions.getServerURIs()[0],
|
||||
clientId,
|
||||
@ -48,27 +69,28 @@ public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle,
|
||||
mqttClient.setCallback(this);
|
||||
mqttClient.connect(connectOptions);
|
||||
|
||||
// 重连后自动重新订阅
|
||||
resubscribeAll();
|
||||
|
||||
running = true;
|
||||
System.out.println("MQTT client connected to: " + connectOptions.getServerURIs()[0]);
|
||||
log.info("MQTT client connected to: {}", connectOptions.getServerURIs()[0]);
|
||||
} catch (MqttException e) {
|
||||
System.err.println("MQTT connection failed: " + e.getMessage());
|
||||
// 添加重试逻辑
|
||||
running = false;
|
||||
log.error("MQTT connection failed: {}", e.getMessage(), e);
|
||||
scheduleReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
cancelReconnectTask();
|
||||
if (mqttClient != null && mqttClient.isConnected()) {
|
||||
try {
|
||||
mqttClient.disconnect();
|
||||
mqttClient.close();
|
||||
} catch (MqttException e) {
|
||||
System.err.println("Error disconnecting MQTT client: " + e.getMessage());
|
||||
log.warn("Error disconnecting MQTT client: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
reconnectExecutor.shutdownNow();
|
||||
running = false;
|
||||
}
|
||||
|
||||
@ -80,9 +102,17 @@ public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle,
|
||||
// MQTT 回调方法
|
||||
@Override
|
||||
public void connectionLost(Throwable cause) {
|
||||
System.err.println("MQTT connection lost: " + cause.getMessage());
|
||||
log.warn("MQTT connection lost: {}", cause == null ? "unknown" : cause.getMessage(), cause);
|
||||
running = false;
|
||||
// 自动重连由 MqttConnectOptions 处理
|
||||
scheduleReconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectComplete(boolean reconnect, String serverURI) {
|
||||
running = true;
|
||||
cancelReconnectTask();
|
||||
log.info("MQTT connection complete, reconnect: {}, serverURI: {}", reconnect, serverURI);
|
||||
resubscribeAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,11 +133,17 @@ public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle,
|
||||
try {
|
||||
if (mqttClient != null && mqttClient.isConnected()) {
|
||||
mqttClient.subscribe(topic, qos);
|
||||
log.info("MQTT subscribe success, topic: {}, qos: {}", topic, qos);
|
||||
} else {
|
||||
log.warn("MQTT subscribe deferred, client not connected, topic: {}", topic);
|
||||
}
|
||||
subscriptions.put(topic, new SubscriptionInfo(listener, qos));
|
||||
} catch (MqttException e) {
|
||||
System.err.println("Subscribe failed: " + e.getMessage());
|
||||
log.error("Subscribe failed, topic: {}, err: {}", topic, e.getMessage(), e);
|
||||
iEmsAlarmRecordsService.addSubFailedAlarmRecord(topic);
|
||||
scheduleReconnect();
|
||||
}
|
||||
iEmsAlarmRecordsService.checkFailedRecord(topic);
|
||||
}
|
||||
|
||||
// 发布方法
|
||||
@ -128,12 +164,52 @@ public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle,
|
||||
subscriptions.forEach((topic, info) -> {
|
||||
try {
|
||||
mqttClient.subscribe(topic, info.getQos());
|
||||
log.info("MQTT resubscribe success, topic: {}, qos: {}", topic, info.getQos());
|
||||
} catch (MqttException e) {
|
||||
System.err.println("Resubscribe failed for topic: " + topic);
|
||||
log.error("Resubscribe failed for topic: {}, err: {}", topic, e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void scheduleReconnect() {
|
||||
if (mqttClient == null || reconnectExecutor.isShutdown()) {
|
||||
return;
|
||||
}
|
||||
if (!reconnectScheduled.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
reconnectFuture = reconnectExecutor.scheduleWithFixedDelay(() -> {
|
||||
if (mqttClient == null) {
|
||||
cancelReconnectTask();
|
||||
return;
|
||||
}
|
||||
if (mqttClient.isConnected()) {
|
||||
cancelReconnectTask();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
log.info("MQTT reconnecting...");
|
||||
mqttClient.connect(connectOptions);
|
||||
running = true;
|
||||
cancelReconnectTask();
|
||||
resubscribeAll();
|
||||
log.info("MQTT reconnect success.");
|
||||
} catch (MqttException e) {
|
||||
running = false;
|
||||
log.warn("MQTT reconnect failed: {}", e.getMessage());
|
||||
}
|
||||
}, RECONNECT_DELAY_SECONDS, RECONNECT_DELAY_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void cancelReconnectTask() {
|
||||
reconnectScheduled.set(false);
|
||||
ScheduledFuture<?> future = reconnectFuture;
|
||||
if (future != null && !future.isCancelled()) {
|
||||
future.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 订阅信息内部类
|
||||
private static class SubscriptionInfo {
|
||||
private final IMqttMessageListener listener;
|
||||
|
||||
@ -1,126 +0,0 @@
|
||||
package com.xzzn.framework.scheduler;
|
||||
|
||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
||||
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
||||
import com.xzzn.ems.mapper.EmsMqttMessageMapper;
|
||||
import com.xzzn.framework.manager.ModbusConnectionManager;
|
||||
import com.xzzn.framework.manager.ModbusConnectionWrapper;
|
||||
import com.xzzn.framework.manager.MqttLifecycleManager;
|
||||
import com.xzzn.framework.web.service.ModbusService;
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@EnableScheduling
|
||||
public class ModbusPoller {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ModbusPoller.class);
|
||||
|
||||
private final MqttLifecycleManager mqttLifecycleManager;
|
||||
|
||||
@Autowired
|
||||
private ModbusConnectionManager connectionManager;
|
||||
@Autowired
|
||||
private ModbusService modbusService;
|
||||
@Autowired
|
||||
private EmsDevicesSettingMapper deviceRepo;
|
||||
@Autowired
|
||||
private EmsMqttMessageMapper emsMqttMessageMapper;
|
||||
|
||||
@Autowired
|
||||
public ModbusPoller(MqttLifecycleManager mqttLifecycleManager) {
|
||||
this.mqttLifecycleManager = mqttLifecycleManager;
|
||||
}
|
||||
// 每5分钟触发(支持cron表达式动态配置)
|
||||
@Scheduled(cron = "${modbus.poll.interval}")
|
||||
@Async("modbusTaskExecutor")
|
||||
public void pollAllDevices() {
|
||||
logger.info("开始执行Modbus设备轮询...");
|
||||
|
||||
List<EmsDevicesSetting> activeDevices = deviceRepo.selectEmsDevicesSettingList(null);
|
||||
|
||||
EmsDevicesSetting device = activeDevices.get(0);
|
||||
try {
|
||||
processData(device,null);
|
||||
} catch (Exception e) {
|
||||
logger.error("调度设备{}任务失败", device.getId(), e);
|
||||
}
|
||||
/*
|
||||
try {
|
||||
pollSingleDevice(device);
|
||||
} catch (Exception e) {
|
||||
logger.error("调度设备{}任务失败", device.getId(), e);
|
||||
}*/
|
||||
/*activeDevices.forEach(device -> {
|
||||
try {
|
||||
CompletableFuture.runAsync(() -> pollSingleDevice(device))
|
||||
.exceptionally(e -> {
|
||||
logger.error("设备{}轮询异常", device.getId(), e);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("调度设备{}任务失败", device.getId(), e);
|
||||
}
|
||||
});*/
|
||||
}
|
||||
|
||||
private void pollSingleDevice(EmsDevicesSetting device) {
|
||||
logger.debug("开始轮询设备: {}", device.getSiteId(), device.getDeviceName(), device.getId());
|
||||
|
||||
ModbusConnectionWrapper wrapper = null;
|
||||
try {
|
||||
// 获取连接
|
||||
wrapper = connectionManager.getConnection(device);
|
||||
|
||||
// 读取保持寄存器
|
||||
int[] data = modbusService.readHoldingRegisters(
|
||||
wrapper.getConnection(),
|
||||
1, //从站ID
|
||||
10 // 寄存器数量
|
||||
);
|
||||
|
||||
// 处理读取到的数据
|
||||
processData(device, data);
|
||||
} catch (Exception e) {
|
||||
logger.error("轮询设备{}失败: {}", device.getId(), e.getMessage());
|
||||
// 标记连接为无效
|
||||
if (wrapper != null) {
|
||||
wrapper.close();
|
||||
connectionManager.removeConnection(Integer.parseInt(device.getDeviceId()));
|
||||
}
|
||||
throw new RuntimeException("轮询设备失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理获取到的数据,发到mqtt服务上
|
||||
private void processData(EmsDevicesSetting device, int[] data) throws MqttException {
|
||||
/*if (data == null || data.length == 0) {
|
||||
logger.warn("设备{}返回空数据", device.getId());
|
||||
return;
|
||||
}*/
|
||||
|
||||
/*// 数据处理逻辑
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("设备[").append(device.getDeviceName()).append("]数据: ");
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
sb.append("R").append(i).append("=").append(data[i]).append(" ");
|
||||
}
|
||||
logger.info(sb.toString());*/
|
||||
|
||||
// 测试发送mqtt
|
||||
/* EmsMqttMessage msg = emsMqttMessageMapper.selectEmsMqttMessageById(1L);
|
||||
String dataJson = msg.getMqttMessage();
|
||||
String topic = msg.getMqttTopic();
|
||||
logger.info("topic:" + topic);
|
||||
logger.info("dataJson:" + dataJson);
|
||||
// 将设备数据下发到mqtt服务器上
|
||||
mqttLifecycleManager.publish(topic, dataJson, 0);*/
|
||||
}
|
||||
}
|
||||
@ -1,170 +0,0 @@
|
||||
package com.xzzn.framework.scheduler;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.xzzn.common.utils.DateUtils;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsStrategyCurve;
|
||||
import com.xzzn.ems.domain.EmsStrategyTemp;
|
||||
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
|
||||
import com.xzzn.ems.domain.vo.StrategyPowerDataVo;
|
||||
import com.xzzn.ems.domain.vo.StrategyRunningVo;
|
||||
import com.xzzn.ems.mapper.*;
|
||||
import com.xzzn.framework.manager.ModbusConnectionManager;
|
||||
import com.xzzn.framework.manager.MqttLifecycleManager;
|
||||
import com.xzzn.framework.web.service.ModbusService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@EnableScheduling
|
||||
public class StrategyPoller {
|
||||
private static final Logger logger = LoggerFactory.getLogger(StrategyPoller.class);
|
||||
|
||||
private final MqttLifecycleManager mqttLifecycleManager;
|
||||
|
||||
@Autowired
|
||||
private ModbusConnectionManager connectionManager;
|
||||
@Autowired
|
||||
private ModbusService modbusService;
|
||||
@Autowired
|
||||
private EmsDevicesSettingMapper deviceRepo;
|
||||
@Autowired
|
||||
private EmsMqttMessageMapper emsMqttMessageMapper;
|
||||
@Autowired
|
||||
private EmsStrategyRunningMapper emsStrategyRunningMapper;
|
||||
@Autowired
|
||||
private EmsStrategyTempMapper emsStrategyTempMapper;
|
||||
@Autowired
|
||||
private EmsStrategyTimeConfigMapper emsStrategyTimeConfigMapper;
|
||||
@Autowired
|
||||
private EmsStrategyCurveMapper emsStrategyCurveMapper;
|
||||
|
||||
@Autowired
|
||||
public StrategyPoller(MqttLifecycleManager mqttLifecycleManager) {
|
||||
this.mqttLifecycleManager = mqttLifecycleManager;
|
||||
}
|
||||
// 每1分钟触发(支持cron表达式动态配置)
|
||||
@Scheduled(cron = "0 */1 * * * *")
|
||||
@Async("strategyTaskExecutor")
|
||||
public void pollAllDevices() {
|
||||
logger.info("开始执行策略数据轮询...");
|
||||
List<StrategyRunningVo> strategyRunningVoList = emsStrategyRunningMapper.getRunningList(null);
|
||||
strategyRunningVoList.forEach(strategyVo -> {
|
||||
try {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
processData(strategyVo);
|
||||
})
|
||||
.exceptionally(e -> {
|
||||
logger.error("策略{}轮询异常", strategyVo.getId(), e);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("策略下方{}任务失败", strategyVo.getId(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 处理获取到的数据,发到mqtt服务上
|
||||
private void processData(StrategyRunningVo strategyVo) {
|
||||
logger.info("策略下发数据处理开始");
|
||||
// 根据运行策略获取主副策略的模板数据
|
||||
Long mainStrategyId = strategyVo.getMainStrategyId();
|
||||
Long auxStrategyId = strategyVo.getAuxStrategyId();
|
||||
String siteId = strategyVo.getSiteId();
|
||||
// 处理主策略数据
|
||||
if (mainStrategyId != null && StringUtils.isNotBlank(siteId)) {
|
||||
dealStrategyCurveData(mainStrategyId, siteId);
|
||||
}
|
||||
// 处理副策略数据
|
||||
if (auxStrategyId != null && StringUtils.isNotBlank(siteId)) {
|
||||
dealStrategyCurveData(auxStrategyId, siteId);
|
||||
}
|
||||
|
||||
// 策略数据下发-下方格式暂无
|
||||
|
||||
logger.info("策略下发结束");
|
||||
}
|
||||
|
||||
private void dealStrategyCurveData(Long mainStrategyId, String siteId) {
|
||||
// 获取当前策略的所有模板
|
||||
List<Map<String, String>> temps = emsStrategyTempMapper.getTempNameList(mainStrategyId,siteId);
|
||||
if (temps != null && temps.size() > 0) {
|
||||
for (Map<String, String> temp : temps) {
|
||||
String tempId = temp.get("templateId");
|
||||
List<EmsStrategyTimeConfig> timeConfigs = emsStrategyTimeConfigMapper.getAllTimeConfigByTempId(tempId);
|
||||
if (timeConfigs != null && timeConfigs.size() > 0) {
|
||||
for (EmsStrategyTimeConfig timeConfig : timeConfigs) {
|
||||
EmsStrategyCurve curve = new EmsStrategyCurve();
|
||||
curve.setStrategyId(mainStrategyId);
|
||||
curve.setSiteId(siteId);
|
||||
curve.setTemplateId(tempId);
|
||||
curve.setCreateBy("system");
|
||||
curve.setCreateTime(DateUtils.getNowDate());
|
||||
curve.setUpdateBy("system");
|
||||
curve.setUpdateTime(DateUtils.getNowDate());
|
||||
// 时间设置
|
||||
int month = Integer.parseInt(timeConfig.getMonth().toString());
|
||||
String[] dateList= dealWithMonth(month);
|
||||
curve.setMonth(Long.valueOf(month));
|
||||
curve.setStartDate(DateUtils.dateTime(DateUtils.YYYY_MM_DD,dateList[0]));
|
||||
curve.setEndDate(DateUtils.dateTime(DateUtils.YYYY_MM_DD,dateList[1]));
|
||||
// powerData-存json格式
|
||||
List<EmsStrategyTemp> powerConfig = emsStrategyTempMapper.selectStrategyTempByTempId(tempId);
|
||||
List<StrategyPowerDataVo> powerDataVoList = new ArrayList<>();
|
||||
for (int i = 0; i < powerConfig.size(); i++) {
|
||||
EmsStrategyTemp powerTemp = powerConfig.get(i);
|
||||
StrategyPowerDataVo powerDataVo = new StrategyPowerDataVo();
|
||||
powerDataVo.setPowerData(powerTemp.getChargeDischargePower());
|
||||
powerDataVo.setEndTime(DateUtils.parseDateToStr("HH:mm:ss",powerTemp.getEndTime()));
|
||||
powerDataVo.setStartTime(DateUtils.parseDateToStr("HH:mm:ss",powerTemp.getStartTime()));
|
||||
powerDataVoList.add(powerDataVo);
|
||||
}
|
||||
curve.setPowerData(powerDataVoList !=null ? JSON.toJSON(powerDataVoList).toString() : "");
|
||||
|
||||
// 记录推送记录
|
||||
emsStrategyCurveMapper.insertEmsStrategyCurve(curve);
|
||||
|
||||
// 设置已下发
|
||||
timeConfig.setIsPost(0);
|
||||
emsStrategyTimeConfigMapper.updateEmsStrategyTimeConfig(timeConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String[] dealWithMonth(int month) {
|
||||
// 获取当前年份
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
|
||||
// 创建YearMonth对象表示当年指定的月份
|
||||
YearMonth yearMonth = YearMonth.of(currentYear, month);
|
||||
|
||||
// 获取当月的第一天和最后一天
|
||||
LocalDate firstDay = yearMonth.atDay(1);
|
||||
LocalDate lastDay = yearMonth.atEndOfMonth();
|
||||
|
||||
// 定义日期格式(年月日)
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
|
||||
// 格式化日期
|
||||
return new String[]{
|
||||
firstDay.format(formatter),
|
||||
lastDay.format(formatter)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
package com.xzzn.framework.web.service;
|
||||
|
||||
import com.ghgande.j2mod.modbus.ModbusException;
|
||||
import com.ghgande.j2mod.modbus.ModbusIOException;
|
||||
import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction;
|
||||
import com.ghgande.j2mod.modbus.msg.ReadInputRegistersRequest;
|
||||
import com.ghgande.j2mod.modbus.msg.ReadInputRegistersResponse;
|
||||
import com.ghgande.j2mod.modbus.net.SerialConnection;
|
||||
import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
|
||||
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Modbus操作服务(添加重试机制)
|
||||
*/
|
||||
@Service
|
||||
public class ModbusService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ModbusService.class);
|
||||
|
||||
@Retryable(
|
||||
value = {ModbusException.class}, // 仅对自定义Modbus异常重试
|
||||
maxAttempts = 3, // 最大重试3次(1次原始调用 + 2次重试)
|
||||
backoff = @Backoff(delay = 1000, multiplier = 2) // 退避策略:1s → 2s → 4s
|
||||
)
|
||||
@CircuitBreaker(name = "modbusOperation", fallbackMethod = "readRegistersFallback")
|
||||
public int[] readHoldingRegisters(Object connection, int startAddr, int count) throws ModbusException {
|
||||
try {
|
||||
if (connection instanceof TCPMasterConnection) {
|
||||
return readTcpRegisters((TCPMasterConnection) connection, startAddr, count);
|
||||
} else if (connection instanceof SerialConnection) {
|
||||
return readRtuRegisters((SerialConnection) connection, startAddr, count);
|
||||
}
|
||||
throw new IllegalArgumentException("不支持的连接类型: " + connection.getClass().getName());
|
||||
} catch (ModbusIOException e) {
|
||||
throw new ModbusException("通信故障", e);
|
||||
} catch (Exception e) {
|
||||
throw new ModbusException("系统错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
private int[] readRtuRegisters(SerialConnection connection, int startAddr, int count) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private int[] readTcpRegisters(TCPMasterConnection conn, int start, int count) throws ModbusException {
|
||||
// 验证连接是否已建立
|
||||
if (!conn.isConnected()) {
|
||||
throw new ModbusIOException("TCP连接未建立");
|
||||
}
|
||||
// 使用正确的功能码(03 - 读取保持寄存器)ReadHoldingRegistersRequest
|
||||
ReadInputRegistersRequest request = new ReadInputRegistersRequest(start, count);
|
||||
ModbusTCPTransaction transaction = new ModbusTCPTransaction(conn);
|
||||
transaction.setRequest(request);
|
||||
|
||||
// 设置超时避免长时间阻塞
|
||||
transaction.setRetries(2);
|
||||
try {
|
||||
transaction.execute();
|
||||
ReadInputRegistersResponse response = (ReadInputRegistersResponse) transaction.getResponse();
|
||||
|
||||
if (response == null) {
|
||||
throw new ModbusException("Modbus异常响应: " + response.getMessage());
|
||||
}
|
||||
|
||||
// 正确解析寄存器值
|
||||
return parseRegisters(response);
|
||||
} catch (ModbusException e) {
|
||||
// 记录详细错误信息
|
||||
logger.error("读取TCP寄存器失败: {}", e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析Modbus响应中的寄存器值
|
||||
*/
|
||||
private int[] parseRegisters(ReadInputRegistersResponse response) {
|
||||
int byteCount = response.getByteCount();
|
||||
int[] result = new int[byteCount / 2];
|
||||
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
// 转换为无符号整数
|
||||
result[i] = response.getRegisterValue(i) & 0xFFFF;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 熔断降级方法
|
||||
*/
|
||||
public int[] readRegistersFallback(Object connection, int startAddr, int count, Exception e) {
|
||||
logger.warn("Modbus操作降级(原因: {}),返回空数据", e.getMessage());
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
package com.xzzn.generator.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -18,10 +17,6 @@ import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.alibaba.druid.DbType;
|
||||
import com.alibaba.druid.sql.SQLUtils;
|
||||
import com.alibaba.druid.sql.ast.SQLStatement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
|
||||
import com.xzzn.common.annotation.Log;
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
@ -29,7 +24,6 @@ import com.xzzn.common.core.page.TableDataInfo;
|
||||
import com.xzzn.common.core.text.Convert;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.common.utils.SecurityUtils;
|
||||
import com.xzzn.common.utils.sql.SqlUtil;
|
||||
import com.xzzn.generator.config.GenConfig;
|
||||
import com.xzzn.generator.domain.GenTable;
|
||||
import com.xzzn.generator.domain.GenTableColumn;
|
||||
@ -121,43 +115,6 @@ public class GenController extends BaseController
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表结构(保存)
|
||||
*/
|
||||
@PreAuthorize("@ss.hasRole('admin')")
|
||||
@Log(title = "创建表", businessType = BusinessType.OTHER)
|
||||
@PostMapping("/createTable")
|
||||
public AjaxResult createTableSave(String sql)
|
||||
{
|
||||
try
|
||||
{
|
||||
SqlUtil.filterKeyword(sql);
|
||||
List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql);
|
||||
List<String> tableNames = new ArrayList<>();
|
||||
for (SQLStatement sqlStatement : sqlStatements)
|
||||
{
|
||||
if (sqlStatement instanceof MySqlCreateTableStatement)
|
||||
{
|
||||
MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement;
|
||||
if (genTableService.createTable(createTableStatement.toString()))
|
||||
{
|
||||
String tableName = createTableStatement.getTableName().replaceAll("`", "");
|
||||
tableNames.add(tableName);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()]));
|
||||
String operName = SecurityUtils.getUsername();
|
||||
genTableService.importGenTable(tableList, operName);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.error(e.getMessage(), e);
|
||||
return AjaxResult.error("创建表结构异常");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改保存代码生成业务
|
||||
*/
|
||||
|
||||
@ -81,11 +81,4 @@ public interface GenTableMapper
|
||||
*/
|
||||
public int deleteGenTableByIds(Long[] ids);
|
||||
|
||||
/**
|
||||
* 创建表
|
||||
*
|
||||
* @param sql 表结构
|
||||
* @return 结果
|
||||
*/
|
||||
public int createTable(String sql);
|
||||
}
|
||||
|
||||
@ -149,18 +149,6 @@ public class GenTableServiceImpl implements IGenTableService
|
||||
genTableColumnMapper.deleteGenTableColumnByIds(tableIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表
|
||||
*
|
||||
* @param sql 创建表语句
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public boolean createTable(String sql)
|
||||
{
|
||||
return genTableMapper.createTable(sql) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入表结构
|
||||
*
|
||||
|
||||
@ -66,14 +66,6 @@ public interface IGenTableService
|
||||
*/
|
||||
public void deleteGenTableByIds(Long[] tableIds);
|
||||
|
||||
/**
|
||||
* 创建表
|
||||
*
|
||||
* @param sql 创建表语句
|
||||
* @return 结果
|
||||
*/
|
||||
public boolean createTable(String sql);
|
||||
|
||||
/**
|
||||
* 导入表结构
|
||||
*
|
||||
|
||||
@ -171,10 +171,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="createTable">
|
||||
${sql}
|
||||
</update>
|
||||
|
||||
<update id="updateGenTable" parameterType="GenTable">
|
||||
update gen_table
|
||||
<set>
|
||||
|
||||
@ -34,6 +34,10 @@
|
||||
<groupId>com.xzzn</groupId>
|
||||
<artifactId>ems-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.xzzn</groupId>
|
||||
<artifactId>ems-framework</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package com.xzzn.quartz.config;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Modbus操作执行器配置
|
||||
* 所有Modbus读写操作必须通过此单线程执行器串行执行,避免并发访问导致通讯故障
|
||||
*/
|
||||
@Configuration
|
||||
public class ModbusExecutorConfig {
|
||||
|
||||
private final ExecutorService modbusExecutor = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = new Thread(r, "modbus-io-thread");
|
||||
t.setDaemon(false);
|
||||
return t;
|
||||
});
|
||||
|
||||
@Bean(name = "modbusExecutor")
|
||||
public ExecutorService modbusExecutor() {
|
||||
return modbusExecutor;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdown() {
|
||||
modbusExecutor.shutdown();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.xzzn.quartz.config;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ScheduledTask {
|
||||
|
||||
|
||||
private ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
|
||||
private final Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
|
||||
|
||||
public void startTask(String deviceId, Runnable task, long period) {
|
||||
stopTask(deviceId); // 如果已有同ID任务在运行,先停止
|
||||
ScheduledFuture<?> future = executor.scheduleAtFixedRate(task, 0, period, TimeUnit.MILLISECONDS);
|
||||
futureMap.put(deviceId, future);
|
||||
}
|
||||
|
||||
public void stopTask(String deviceId) {
|
||||
ScheduledFuture<?> future = futureMap.get(deviceId);
|
||||
if (future != null && !future.isDone()) {
|
||||
future.cancel(true);
|
||||
}
|
||||
futureMap.remove(deviceId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.xzzn.quartz.task;
|
||||
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.service.impl.DeviceDataProcessServiceImpl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("calcPointTask")
|
||||
public class CalcPointTask {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CalcPointTask.class);
|
||||
|
||||
@Autowired
|
||||
private DeviceDataProcessServiceImpl deviceDataProcessService;
|
||||
|
||||
public void syncAllSites() {
|
||||
log.info("开始执行计算点轮询任务-全站点");
|
||||
deviceDataProcessService.syncCalcPointDataFromInfluxByQuartz();
|
||||
log.info("结束执行计算点轮询任务-全站点");
|
||||
}
|
||||
|
||||
public void syncBySite(String siteId) {
|
||||
if (StringUtils.isBlank(siteId)) {
|
||||
log.warn("计算点轮询任务参数为空,转为全站点执行");
|
||||
syncAllSites();
|
||||
return;
|
||||
}
|
||||
String normalizedSiteId = siteId.trim();
|
||||
log.info("开始执行计算点轮询任务-单站点, siteId: {}", normalizedSiteId);
|
||||
deviceDataProcessService.syncCalcPointDataFromInfluxByQuartz(normalizedSiteId);
|
||||
log.info("结束执行计算点轮询任务-单站点, siteId: {}", normalizedSiteId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.xzzn.quartz.task;
|
||||
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.service.impl.DeviceDataProcessServiceImpl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("dailyChargeDataTask")
|
||||
public class DailyChargeDataTask {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DailyChargeDataTask.class);
|
||||
|
||||
@Autowired
|
||||
private DeviceDataProcessServiceImpl deviceDataProcessService;
|
||||
|
||||
public void syncAllSites() {
|
||||
log.info("开始执行每日充放电数据轮询任务-全站点");
|
||||
deviceDataProcessService.syncDailyChargeDataFromInfluxByQuartz();
|
||||
log.info("结束执行每日充放电数据轮询任务-全站点");
|
||||
}
|
||||
|
||||
public void syncBySite(String siteId) {
|
||||
if (StringUtils.isBlank(siteId)) {
|
||||
log.warn("每日充放电数据轮询任务参数为空,转为全站点执行");
|
||||
syncAllSites();
|
||||
return;
|
||||
}
|
||||
String normalizedSiteId = siteId.trim();
|
||||
log.info("开始执行每日充放电数据轮询任务-单站点, siteId: {}", normalizedSiteId);
|
||||
deviceDataProcessService.syncDailyChargeDataFromInfluxByQuartz(normalizedSiteId);
|
||||
log.info("结束执行每日充放电数据轮询任务-单站点, siteId: {}", normalizedSiteId);
|
||||
}
|
||||
}
|
||||
486
ems-quartz/src/main/java/com/xzzn/quartz/task/ModbusPoller.java
Normal file
486
ems-quartz/src/main/java/com/xzzn/quartz/task/ModbusPoller.java
Normal file
@ -0,0 +1,486 @@
|
||||
package com.xzzn.quartz.task;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.serotonin.modbus4j.ModbusMaster;
|
||||
import com.xzzn.common.constant.RedisKeyConstants;
|
||||
import com.xzzn.common.core.modbus.ModbusProcessor;
|
||||
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
||||
import com.xzzn.common.core.modbus.domain.TagConfig;
|
||||
import com.xzzn.common.core.redis.RedisCache;
|
||||
import com.xzzn.common.enums.DeviceRunningStatus;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
||||
import com.xzzn.ems.domain.EmsPointConfig;
|
||||
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
||||
import com.xzzn.ems.mapper.EmsPointConfigMapper;
|
||||
import com.xzzn.ems.service.IEmsAlarmRecordsService;
|
||||
import com.xzzn.ems.service.impl.DeviceDataProcessServiceImpl;
|
||||
import com.xzzn.framework.web.service.MqttPublisher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* 轮询设备-通过modbus协议读取数据
|
||||
*/
|
||||
@Component("modbusPoller")
|
||||
public class ModbusPoller {
|
||||
private static final Logger log = LoggerFactory.getLogger(ModbusPoller.class);
|
||||
private static final int SITE_DEVICE_OFFLINE_THRESHOLD = 6;
|
||||
|
||||
private final Map<String, Integer> deviceFailureCounts = new ConcurrentHashMap<>();
|
||||
private final AtomicBoolean polling = new AtomicBoolean(false);
|
||||
|
||||
@Resource(name = "modbusExecutor")
|
||||
private ExecutorService modbusExecutor;
|
||||
|
||||
@Autowired
|
||||
private ModbusProcessor modbusProcessor;
|
||||
@Autowired
|
||||
private IEmsAlarmRecordsService iEmsAlarmRecordsService;
|
||||
|
||||
@Autowired
|
||||
private DeviceDataProcessServiceImpl deviceDataProcessServiceImpl;
|
||||
@Autowired
|
||||
private EmsDevicesSettingMapper emsDevicesSettingMapper;
|
||||
@Autowired
|
||||
private EmsPointConfigMapper emsPointConfigMapper;
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
@Value("${mqtt.topic}")
|
||||
private String topic;
|
||||
|
||||
public void pollAllDevices() {
|
||||
if (!polling.compareAndSet(false, true)) {
|
||||
log.warn("上一次轮询尚未完成,本次轮询跳过");
|
||||
return;
|
||||
}
|
||||
List<PollingTask> pollingTasks = buildPollingTasks();
|
||||
if (CollectionUtils.isEmpty(pollingTasks)) {
|
||||
log.warn("未查询到可用的Modbus采集点位配置,跳过本轮轮询");
|
||||
polling.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 按主机IP分组,同一网关串行访问,避免连接抖动
|
||||
Map<String, List<PollingTask>> groupedByHost = pollingTasks.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
task -> task.getDeviceConfig().getHost(),
|
||||
HashMap::new,
|
||||
Collectors.toList()));
|
||||
|
||||
Future<?> future = modbusExecutor.submit(() -> {
|
||||
for (Map.Entry<String, List<PollingTask>> entry : groupedByHost.entrySet()) {
|
||||
String hostKey = entry.getKey();
|
||||
List<PollingTask> tasks = entry.getValue();
|
||||
for (PollingTask task : tasks) {
|
||||
try {
|
||||
scheduledStart(task.getSiteId(), task.getDeviceConfig());
|
||||
// 每次读取后等待200ms,给Modbus网关足够的处理时间
|
||||
Thread.sleep(200);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("Modbus轮询被中断");
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
log.error("采集设备数据异常: siteId={}, deviceId={}",
|
||||
task.getSiteId(), task.getDeviceConfig().getDeviceNumber(), e);
|
||||
}
|
||||
}
|
||||
log.info("采集设备数据{}轮询任务执行完成", hostKey);
|
||||
}
|
||||
});
|
||||
try {
|
||||
future.get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.warn("Modbus轮询任务等待中断");
|
||||
} catch (Exception e) {
|
||||
log.error("Modbus轮询任务执行异常", e);
|
||||
} finally {
|
||||
polling.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
private List<PollingTask> buildPollingTasks() {
|
||||
List<EmsPointConfig> pointConfigs = emsPointConfigMapper.selectModbusCollectPointConfigs(null);
|
||||
if (CollectionUtils.isEmpty(pointConfigs)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<EmsDevicesSetting> allDevices = emsDevicesSettingMapper.selectEmsDevicesSettingList(null);
|
||||
Map<String, EmsDevicesSetting> deviceMap = allDevices.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(device -> StringUtils.isNoneBlank(device.getSiteId(), device.getDeviceId()))
|
||||
.collect(Collectors.toMap(
|
||||
this::buildSiteDeviceKey,
|
||||
device -> device,
|
||||
(left, right) -> left));
|
||||
|
||||
Map<String, List<EmsPointConfig>> pointsByDevice = pointConfigs.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(point -> StringUtils.isNoneBlank(point.getSiteId(), point.getDeviceId()))
|
||||
.collect(Collectors.groupingBy(
|
||||
point -> point.getSiteId() + "_" + point.getDeviceId(),
|
||||
HashMap::new,
|
||||
Collectors.toList()));
|
||||
|
||||
List<PollingTask> tasks = new ArrayList<>();
|
||||
for (Map.Entry<String, List<EmsPointConfig>> entry : pointsByDevice.entrySet()) {
|
||||
String siteDeviceKey = entry.getKey();
|
||||
EmsDevicesSetting device = deviceMap.get(siteDeviceKey);
|
||||
if (device == null) {
|
||||
log.warn("未找到设备连接配置,跳过采集: key={}", siteDeviceKey);
|
||||
continue;
|
||||
}
|
||||
DeviceConfig deviceConfig = buildDeviceConfig(device, entry.getValue());
|
||||
if (deviceConfig == null) {
|
||||
continue;
|
||||
}
|
||||
tasks.add(new PollingTask(device.getSiteId(), deviceConfig));
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
|
||||
private DeviceConfig buildDeviceConfig(EmsDevicesSetting device, List<EmsPointConfig> pointConfigs) {
|
||||
if (device == null || CollectionUtils.isEmpty(pointConfigs)) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(device.getIpAddress()) || device.getIpPort() == null || device.getSlaveId() == null) {
|
||||
log.warn("设备连接参数不完整,跳过采集: siteId={}, deviceId={}", device.getSiteId(), device.getDeviceId());
|
||||
return null;
|
||||
}
|
||||
List<TagConfig> tags = pointConfigs.stream()
|
||||
.sorted(Comparator.comparing(point -> point.getModbusReadOrder() == null ? 0 : point.getModbusReadOrder()))
|
||||
.map(this::toTagConfig)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(tags)) {
|
||||
log.warn("设备无有效Modbus点位配置,跳过采集: siteId={}, deviceId={}", device.getSiteId(), device.getDeviceId());
|
||||
return null;
|
||||
}
|
||||
|
||||
DeviceConfig deviceConfig = new DeviceConfig();
|
||||
deviceConfig.setEnabled(true);
|
||||
deviceConfig.setDeviceName(device.getDeviceName());
|
||||
deviceConfig.setDeviceNumber(device.getDeviceId());
|
||||
deviceConfig.setHost(device.getIpAddress());
|
||||
deviceConfig.setPort(device.getIpPort().intValue());
|
||||
deviceConfig.setSlaveId(device.getSlaveId().intValue());
|
||||
deviceConfig.setTags(tags);
|
||||
return deviceConfig;
|
||||
}
|
||||
|
||||
private TagConfig toTagConfig(EmsPointConfig pointConfig) {
|
||||
if (pointConfig == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(pointConfig.getDataKey())) {
|
||||
return null;
|
||||
}
|
||||
String address = normalizeAddress(pointConfig.getRegisterAddress(), pointConfig.getModbusRegisterType());
|
||||
if (StringUtils.isBlank(address)) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isBlank(pointConfig.getModbusDataType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TagConfig tag = new TagConfig();
|
||||
tag.setKey(pointConfig.getDataKey().trim());
|
||||
tag.setAddress(address);
|
||||
tag.setDataType(pointConfig.getModbusDataType().trim());
|
||||
tag.setA(pointConfig.getDataA() == null ? 0F : pointConfig.getDataA().floatValue());
|
||||
tag.setK(pointConfig.getDataK() == null ? 1F : pointConfig.getDataK().floatValue());
|
||||
tag.setB(pointConfig.getDataB() == null ? 0F : pointConfig.getDataB().floatValue());
|
||||
tag.setBit(pointConfig.getDataBit());
|
||||
return tag;
|
||||
}
|
||||
|
||||
private String normalizeAddress(String registerAddress, String registerType) {
|
||||
if (StringUtils.isBlank(registerAddress)) {
|
||||
return null;
|
||||
}
|
||||
String normalizedAddress = registerAddress.trim();
|
||||
if (!normalizedAddress.chars().allMatch(Character::isDigit)) {
|
||||
log.warn("寄存器地址必须为数字,当前值: {}", normalizedAddress);
|
||||
return null;
|
||||
}
|
||||
if (normalizedAddress.length() > 1) {
|
||||
char first = normalizedAddress.charAt(0);
|
||||
if (first >= '0' && first <= '4') {
|
||||
return normalizedAddress;
|
||||
}
|
||||
}
|
||||
return getRegisterPrefix(registerType) + normalizedAddress;
|
||||
}
|
||||
|
||||
private String getRegisterPrefix(String registerType) {
|
||||
String normalized = StringUtils.defaultString(registerType).trim().toUpperCase();
|
||||
switch (normalized) {
|
||||
case "COIL":
|
||||
return "0";
|
||||
case "DISCRETE_INPUT":
|
||||
return "1";
|
||||
case "INPUT_REGISTER":
|
||||
return "3";
|
||||
case "HOLDING_REGISTER":
|
||||
default:
|
||||
return "4";
|
||||
}
|
||||
}
|
||||
|
||||
private String buildSiteDeviceKey(EmsDevicesSetting device) {
|
||||
return device.getSiteId() + "_" + device.getDeviceId();
|
||||
}
|
||||
|
||||
public void scheduledStart(String siteId, DeviceConfig config) {
|
||||
if (config.isEnabled()) {
|
||||
log.info("Reading data from devices: {}", config.getDeviceName());
|
||||
|
||||
// 带重试的读取,最多重试2次
|
||||
Map<String, Object> data = readWithRetry(config, 2);
|
||||
|
||||
List<String> rawValuEmptyList = new ArrayList<>();
|
||||
// 在这里处理采集到的数据空
|
||||
config.getTags().forEach(tag -> {
|
||||
Object rawValue = data.get(tag.getKey());
|
||||
if (rawValue != null) {
|
||||
float value = 0;
|
||||
if (rawValue instanceof Number) {
|
||||
value = ((Number) rawValue).floatValue(); // 安全地转换为 float
|
||||
} else {
|
||||
log.error("tag:{},无法将数据转换为数字: {}", tag.getKey(), rawValue);
|
||||
}
|
||||
value = tag.getA() * value * value + tag.getK() * value + tag.getB();
|
||||
|
||||
int intValue = (int) value;
|
||||
if (tag.getBit() != null) {
|
||||
log.info("tag:{},bit:{},value:{}", tag.getKey(), tag.getBit(), value);
|
||||
String binary = Integer.toBinaryString(intValue);
|
||||
data.put(tag.getKey(), binary);
|
||||
} else {
|
||||
data.put(tag.getKey(), value);
|
||||
}
|
||||
} else {
|
||||
// data.put(tag.getKey(), rawValue);
|
||||
// log.warn("tag:{},数据为空: {}", tag.getKey(), rawValue);
|
||||
rawValuEmptyList.add("tag: " + tag.getKey() + ",数据为空: " + rawValue);
|
||||
}
|
||||
});
|
||||
if (!rawValuEmptyList.isEmpty()) {
|
||||
log.warn("设备 {} 数据为空: {}", config.getDeviceName(), JSON.toJSONString(rawValuEmptyList));
|
||||
}
|
||||
log.info("Data from {}: {}", config.getDeviceName(), data);
|
||||
String deviceNumber = config.getDeviceNumber();
|
||||
//处理数据并发送MQTT消息、保存Redis数据和数据入库
|
||||
processingData(siteId, data, deviceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 带重试的读取方法
|
||||
*/
|
||||
private Map<String, Object> readWithRetry(DeviceConfig config, int maxRetries) {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
|
||||
for (int attempt = 0; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
ModbusMaster master = modbusProcessor.borrowMaster(config);
|
||||
data = modbusProcessor.readDataFromDevice(config, master);
|
||||
|
||||
// 如果读取成功(有数据),直接返回
|
||||
if (!data.isEmpty()) {
|
||||
if (attempt > 0) {
|
||||
log.info("设备 {} 第 {} 次重试成功", config.getDeviceName(), attempt);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
// 读取返回空数据,等待后重试
|
||||
if (attempt < maxRetries) {
|
||||
log.warn("设备 {} 读取返回空数据,等待1秒后重试 ({}/{})",
|
||||
config.getDeviceName(), attempt + 1, maxRetries);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("设备 {} 读取异常 ({}/{}): {}",
|
||||
config.getDeviceName(), attempt + 1, maxRetries, e.getMessage());
|
||||
|
||||
if (attempt < maxRetries) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 所有重试都失败
|
||||
log.error("设备 {} 读取失败,已重试 {} 次", config.getDeviceName(), maxRetries);
|
||||
return data;
|
||||
}
|
||||
|
||||
private void processingData(String siteId, Map<String, Object> data, String deviceNumber) {
|
||||
String siteDeviceKey = siteId + "_" + deviceNumber;
|
||||
if (CollectionUtils.isEmpty(data)) {
|
||||
// 增加失败计数
|
||||
int failureCount = deviceFailureCounts.getOrDefault(siteDeviceKey, 0) + 1;
|
||||
deviceFailureCounts.put(siteDeviceKey, failureCount);
|
||||
|
||||
log.warn("设备 {} 数据读取失败,当前连续失败次数: {}", siteDeviceKey, failureCount);
|
||||
|
||||
// 连续6次失败触发报警
|
||||
if (failureCount >= SITE_DEVICE_OFFLINE_THRESHOLD) {
|
||||
addDeviceOfflineRecord(siteId, deviceNumber);
|
||||
log.error("设备 {} 连续 {} 次未读取到数据,触发报警", siteDeviceKey, failureCount);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 数据读取成功,重置计数器
|
||||
deviceFailureCounts.remove(siteDeviceKey);
|
||||
// 读取到数据后告警自恢复
|
||||
deleteDeviceOfflineRecord(siteId, deviceNumber);
|
||||
|
||||
// 发送MQTT消息、保存Redis数据和数据入库
|
||||
Long timestamp = System.currentTimeMillis();
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("Data", data);
|
||||
json.put("timestamp", timestamp);
|
||||
json.put("Device", deviceNumber);
|
||||
if (shouldSendMqttOnChange(siteId, deviceNumber, data)) {
|
||||
sendMqttMsg(json);
|
||||
} else {
|
||||
sendMqttHeartbeat(deviceNumber, timestamp);
|
||||
log.info("设备 {} 数据无变化,已发送心跳MQTT", siteDeviceKey);
|
||||
}
|
||||
saveRedisData(siteId, json, deviceNumber);
|
||||
saveDataToDatabase(siteId, data, deviceNumber, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 逢变上送:仅当Data发生变化时才发送MQTT
|
||||
*/
|
||||
private boolean shouldSendMqttOnChange(String siteId, String deviceNumber, Map<String, Object> currentData) {
|
||||
JSONObject lastPayload = redisCache.getCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA + siteId + "_" + deviceNumber);
|
||||
if (lastPayload == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Object lastDataObj = lastPayload.get("Data");
|
||||
if (lastDataObj == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSONObject lastData = JSON.parseObject(JSON.toJSONString(lastDataObj));
|
||||
JSONObject currentDataJson = JSON.parseObject(JSON.toJSONString(currentData));
|
||||
return !Objects.equals(lastData, currentDataJson);
|
||||
}
|
||||
|
||||
private void sendMqttHeartbeat(String deviceNumber, Long timestamp) {
|
||||
JSONObject heartbeat = new JSONObject();
|
||||
heartbeat.put("Device", deviceNumber);
|
||||
heartbeat.put("timestamp", timestamp);
|
||||
heartbeat.put("Heartbeat", 1);
|
||||
heartbeat.put("Data", new JSONObject());
|
||||
sendMqttMsg(heartbeat);
|
||||
}
|
||||
|
||||
public void sendMqttMsg(JSONObject json) {
|
||||
try {
|
||||
mqttPublisher.publish(topic, Collections.singletonList(json).toString(), 0);
|
||||
} catch (MqttException e) {
|
||||
log.error("MQTT消息发布失败: {}, reason code: {}", json.toJSONString(), e.getReasonCode() ,e);
|
||||
}
|
||||
log.info("已发送数据: {}", json.toJSONString());
|
||||
}
|
||||
|
||||
|
||||
public void saveRedisData(String siteId, JSONObject obj, String deviceNumber) {
|
||||
try {
|
||||
// 存放mqtt原始每个设备最晚一次数据,便于后面点位获取数据
|
||||
redisCache.setCacheObject(RedisKeyConstants.ORIGINAL_MQTT_DATA + siteId + "_" + deviceNumber, obj);
|
||||
// 存放每次同步数据,失效时间(同同步时间)-用于判断是否正常同步数据和保护策略查询
|
||||
redisCache.setCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceNumber, obj, 1, TimeUnit.MINUTES);
|
||||
log.info("数据已成功存储在Redis: {}", deviceNumber);
|
||||
} catch (Exception e) {
|
||||
log.error("无法在设备的Redis中存储数据: {}", deviceNumber, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDataToDatabase(String siteId, Map<String, Object> data, String deviceNumber, Long timestamp) {
|
||||
JSONObject payload = new JSONObject();
|
||||
payload.put("Device", deviceNumber);
|
||||
payload.put("Data", JSON.toJSONString(data));
|
||||
payload.put("timestamp", timestamp);
|
||||
deviceDataProcessServiceImpl.handleDeviceData(Collections.singletonList(payload).toString(), siteId);
|
||||
}
|
||||
|
||||
//处理设备连接失败的情况,更新设备状态为离线,添加报警记录
|
||||
private void addDeviceOfflineRecord(String siteId, String deviceNumber) {
|
||||
updateDeviceStatus(siteId, deviceNumber, DeviceRunningStatus.OFFLINE.getCode());
|
||||
iEmsAlarmRecordsService.addDeviceOfflineRecord(siteId, deviceNumber);
|
||||
}
|
||||
|
||||
//处理设备读取到数据的情况,更新设备状态为在线,报警记录自恢复
|
||||
private void deleteDeviceOfflineRecord(String siteId, String deviceNumber) {
|
||||
updateDeviceStatus(siteId, deviceNumber, DeviceRunningStatus.ONLINE.getCode());
|
||||
iEmsAlarmRecordsService.deleteDeviceOfflineRecord(siteId, deviceNumber);
|
||||
}
|
||||
|
||||
// 更新设备状态为在线或离线
|
||||
private void updateDeviceStatus(String siteId, String deviceNumber, String deviceStatus) {
|
||||
EmsDevicesSetting emsDevicesSetting = emsDevicesSettingMapper.getDeviceBySiteAndDeviceId(deviceNumber, siteId);
|
||||
if (emsDevicesSetting != null && !Objects.equals(emsDevicesSetting.getDeviceStatus(), deviceStatus)) {
|
||||
emsDevicesSetting.setDeviceStatus(deviceStatus);
|
||||
emsDevicesSettingMapper.updateEmsDevicesSetting(emsDevicesSetting);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PollingTask {
|
||||
private final String siteId;
|
||||
private final DeviceConfig deviceConfig;
|
||||
|
||||
private PollingTask(String siteId, DeviceConfig deviceConfig) {
|
||||
this.siteId = siteId;
|
||||
this.deviceConfig = deviceConfig;
|
||||
}
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public DeviceConfig getDeviceConfig() {
|
||||
return deviceConfig;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,616 @@
|
||||
package com.xzzn.quartz.task;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xzzn.common.annotation.SyncAfterInsert;
|
||||
import com.xzzn.common.constant.RedisKeyConstants;
|
||||
import com.xzzn.common.core.redis.RedisCache;
|
||||
import com.xzzn.common.enums.AlarmLevelStatus;
|
||||
import com.xzzn.common.enums.AlarmStatus;
|
||||
import com.xzzn.common.enums.ProtPlanStatus;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsAlarmRecords;
|
||||
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
|
||||
import com.xzzn.ems.domain.vo.ProtectionConstraintVo;
|
||||
import com.xzzn.ems.domain.vo.ProtectionPlanVo;
|
||||
import com.xzzn.ems.domain.vo.ProtectionSettingVo;
|
||||
import com.xzzn.ems.domain.vo.ProtectionSettingsGroupVo;
|
||||
import com.xzzn.ems.mapper.EmsAlarmRecordsMapper;
|
||||
import com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper;
|
||||
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 告警保护方案轮询
|
||||
*/
|
||||
@Component("protectionPlanTask")
|
||||
public class ProtectionPlanTask {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProtectionPlanTask.class);
|
||||
private static final BigDecimal DEFAULT_L1_POWER_RATIO = new BigDecimal("0.5");
|
||||
private static final int CONSTRAINT_TTL_SECONDS = 120;
|
||||
|
||||
@Resource(name = "scheduledExecutorService")
|
||||
private ScheduledExecutorService scheduledExecutorService;
|
||||
@Autowired
|
||||
private IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService;
|
||||
@Autowired
|
||||
private EmsAlarmRecordsMapper emsAlarmRecordsMapper;
|
||||
@Autowired
|
||||
private EmsFaultProtectionPlanMapper emsFaultProtectionPlanMapper;
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public ProtectionPlanTask(IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService) {
|
||||
this.iEmsFaultProtectionPlanService = iEmsFaultProtectionPlanService;
|
||||
}
|
||||
|
||||
public void pollPlanList() {
|
||||
Long planId = 0L;
|
||||
try {
|
||||
List<EmsFaultProtectionPlan> planList = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(null);
|
||||
for (EmsFaultProtectionPlan plan : planList) {
|
||||
planId = plan.getId();
|
||||
String siteId = plan.getSiteId();
|
||||
if (StringUtils.isEmpty(siteId)) {
|
||||
continue;
|
||||
}
|
||||
ProtectionSettingsGroupVo settingGroup = parseProtectionSettings(plan.getProtectionSettings());
|
||||
if (CollectionUtils.isEmpty(settingGroup.getFaultSettings())
|
||||
&& CollectionUtils.isEmpty(settingGroup.getReleaseSettings())) {
|
||||
continue;
|
||||
}
|
||||
dealWithProtectionPlan(plan, settingGroup);
|
||||
}
|
||||
refreshProtectionConstraintCache(planList);
|
||||
} catch (Exception e) {
|
||||
logger.error("轮询失败,方案id为:{}", planId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SyncAfterInsert
|
||||
private void dealWithProtectionPlan(EmsFaultProtectionPlan plan, ProtectionSettingsGroupVo settingGroup) {
|
||||
logger.info("<轮询保护方案> 站点:{},方案ID:{}", plan.getSiteId(), plan.getId());
|
||||
String siteId = plan.getSiteId();
|
||||
Integer isAlertAlarm = plan.getIsAlert();
|
||||
List<ProtectionSettingVo> faultSettings = settingGroup.getFaultSettings();
|
||||
List<ProtectionSettingVo> releaseSettings = settingGroup.getReleaseSettings();
|
||||
Long status = plan.getStatus();
|
||||
if (status == null) {
|
||||
status = ProtPlanStatus.STOP.getCode();
|
||||
}
|
||||
|
||||
if (Objects.equals(status, ProtPlanStatus.STOP.getCode())) {
|
||||
if (checkIsNeedIssuedPlan(faultSettings, siteId)) {
|
||||
int faultDelay = safeDelaySeconds(plan.getFaultDelaySeconds(), 0);
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
if (!checkIsNeedIssuedPlan(faultSettings, siteId)) {
|
||||
return;
|
||||
}
|
||||
if (Integer.valueOf(1).equals(isAlertAlarm)) {
|
||||
EmsAlarmRecords alarmRecords = addAlarmRecord(siteId, plan.getFaultName(), getAlarmLevel(plan.getFaultLevel()));
|
||||
emsAlarmRecordsMapper.insertEmsAlarmRecords(alarmRecords);
|
||||
}
|
||||
plan.setStatus(ProtPlanStatus.RUNNING.getCode());
|
||||
plan.setUpdateBy("system");
|
||||
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
|
||||
refreshSiteProtectionConstraint(siteId);
|
||||
}, faultDelay, TimeUnit.SECONDS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkIsNeedCancelPlan(releaseSettings, siteId)) {
|
||||
int releaseDelay = safeDelaySeconds(plan.getReleaseDelaySeconds(), 0);
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
if (Integer.valueOf(1).equals(isAlertAlarm)) {
|
||||
EmsAlarmRecords emsAlarmRecords = emsAlarmRecordsMapper.getFailedRecord(
|
||||
siteId,
|
||||
plan.getFaultName(),
|
||||
getAlarmLevel(plan.getFaultLevel())
|
||||
);
|
||||
if (emsAlarmRecords != null) {
|
||||
emsAlarmRecords.setStatus(AlarmStatus.DONE.getCode());
|
||||
emsAlarmRecordsMapper.updateEmsAlarmRecords(emsAlarmRecords);
|
||||
}
|
||||
}
|
||||
plan.setStatus(ProtPlanStatus.STOP.getCode());
|
||||
plan.setUpdateBy("system");
|
||||
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
|
||||
refreshSiteProtectionConstraint(siteId);
|
||||
}, releaseDelay, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private int safeDelaySeconds(Long delay, int defaultSeconds) {
|
||||
if (delay == null || delay < 0) {
|
||||
return defaultSeconds;
|
||||
}
|
||||
return delay.intValue();
|
||||
}
|
||||
|
||||
private ProtectionSettingsGroupVo parseProtectionSettings(String settingsJson) {
|
||||
if (StringUtils.isEmpty(settingsJson)) {
|
||||
return ProtectionSettingsGroupVo.empty();
|
||||
}
|
||||
try {
|
||||
if (settingsJson.trim().startsWith("[")) {
|
||||
List<ProtectionSettingVo> legacy = objectMapper.readValue(
|
||||
settingsJson,
|
||||
new TypeReference<List<ProtectionSettingVo>>() {}
|
||||
);
|
||||
ProtectionSettingsGroupVo groupVo = ProtectionSettingsGroupVo.empty();
|
||||
groupVo.setFaultSettings(legacy);
|
||||
groupVo.setReleaseSettings(legacy);
|
||||
return groupVo;
|
||||
}
|
||||
ProtectionSettingsGroupVo groupVo = objectMapper.readValue(settingsJson, ProtectionSettingsGroupVo.class);
|
||||
if (groupVo == null) {
|
||||
return ProtectionSettingsGroupVo.empty();
|
||||
}
|
||||
if (groupVo.getFaultSettings() == null) {
|
||||
groupVo.setFaultSettings(new ArrayList<>());
|
||||
}
|
||||
if (groupVo.getReleaseSettings() == null) {
|
||||
groupVo.setReleaseSettings(new ArrayList<>());
|
||||
}
|
||||
return groupVo;
|
||||
} catch (Exception e) {
|
||||
logger.error("解析保护前提失败,json:{}", settingsJson, e);
|
||||
return ProtectionSettingsGroupVo.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private List<ProtectionPlanVo> parseProtectionPlan(String planJson) {
|
||||
if (StringUtils.isEmpty(planJson)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
if (planJson.trim().startsWith("[")) {
|
||||
List<ProtectionPlanVo> plans = objectMapper.readValue(
|
||||
planJson,
|
||||
new TypeReference<List<ProtectionPlanVo>>() {}
|
||||
);
|
||||
return plans == null ? new ArrayList<>() : plans;
|
||||
}
|
||||
ProtectionPlanVo plan = objectMapper.readValue(planJson, ProtectionPlanVo.class);
|
||||
List<ProtectionPlanVo> plans = new ArrayList<>();
|
||||
if (plan != null) {
|
||||
plans.add(plan);
|
||||
}
|
||||
return plans;
|
||||
} catch (Exception e) {
|
||||
logger.error("解析执行保护失败,json:{}", planJson, e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshProtectionConstraintCache(List<EmsFaultProtectionPlan> allPlans) {
|
||||
Map<String, List<EmsFaultProtectionPlan>> planBySite = new HashMap<>();
|
||||
for (EmsFaultProtectionPlan plan : allPlans) {
|
||||
if (StringUtils.isEmpty(plan.getSiteId())) {
|
||||
continue;
|
||||
}
|
||||
planBySite.computeIfAbsent(plan.getSiteId(), k -> new ArrayList<>()).add(plan);
|
||||
}
|
||||
for (Map.Entry<String, List<EmsFaultProtectionPlan>> entry : planBySite.entrySet()) {
|
||||
writeSiteProtectionConstraint(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshSiteProtectionConstraint(String siteId) {
|
||||
if (StringUtils.isEmpty(siteId)) {
|
||||
return;
|
||||
}
|
||||
EmsFaultProtectionPlan query = new EmsFaultProtectionPlan();
|
||||
query.setSiteId(siteId);
|
||||
List<EmsFaultProtectionPlan> sitePlans = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(query);
|
||||
writeSiteProtectionConstraint(siteId, sitePlans);
|
||||
}
|
||||
|
||||
private void writeSiteProtectionConstraint(String siteId, List<EmsFaultProtectionPlan> sitePlans) {
|
||||
List<EmsFaultProtectionPlan> runningPlans = new ArrayList<>();
|
||||
for (EmsFaultProtectionPlan plan : sitePlans) {
|
||||
if (Objects.equals(plan.getStatus(), ProtPlanStatus.RUNNING.getCode())) {
|
||||
runningPlans.add(plan);
|
||||
}
|
||||
}
|
||||
|
||||
String key = RedisKeyConstants.PROTECTION_CONSTRAINT + siteId;
|
||||
if (runningPlans.isEmpty()) {
|
||||
redisCache.deleteObject(key);
|
||||
return;
|
||||
}
|
||||
|
||||
ProtectionConstraintVo merged = ProtectionConstraintVo.empty();
|
||||
for (EmsFaultProtectionPlan runningPlan : runningPlans) {
|
||||
ProtectionConstraintVo single = buildConstraintFromPlan(runningPlan);
|
||||
mergeConstraint(merged, single);
|
||||
}
|
||||
merged.setUpdateAt(System.currentTimeMillis());
|
||||
redisCache.setCacheObject(key, merged, CONSTRAINT_TTL_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private ProtectionConstraintVo buildConstraintFromPlan(EmsFaultProtectionPlan plan) {
|
||||
ProtectionConstraintVo vo = ProtectionConstraintVo.empty();
|
||||
int level = plan.getFaultLevel() == null ? 0 : plan.getFaultLevel();
|
||||
vo.setLevel(level);
|
||||
vo.setSourcePlanIds(new ArrayList<>());
|
||||
vo.getSourcePlanIds().add(plan.getId());
|
||||
|
||||
if (level == 1) {
|
||||
vo.setPowerLimitRatio(DEFAULT_L1_POWER_RATIO);
|
||||
} else if (level >= 3) {
|
||||
vo.setForceStop(true);
|
||||
vo.setForceStandby(true);
|
||||
vo.setAllowCharge(false);
|
||||
vo.setAllowDischarge(false);
|
||||
vo.setPowerLimitRatio(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
String description = StringUtils.isEmpty(plan.getDescription()) ? "" : plan.getDescription();
|
||||
BigDecimal ratioByDesc = parseDerateRatio(description);
|
||||
if (ratioByDesc != null) {
|
||||
vo.setPowerLimitRatio(minRatio(vo.getPowerLimitRatio(), ratioByDesc));
|
||||
}
|
||||
|
||||
if (description.contains("禁止充放电")) {
|
||||
vo.setAllowCharge(false);
|
||||
vo.setAllowDischarge(false);
|
||||
} else {
|
||||
if (description.contains("禁止充电")) {
|
||||
vo.setAllowCharge(false);
|
||||
}
|
||||
if (description.contains("禁止放电")) {
|
||||
vo.setAllowDischarge(false);
|
||||
}
|
||||
if (description.contains("允许充电")) {
|
||||
vo.setAllowCharge(true);
|
||||
}
|
||||
if (description.contains("允许放电")) {
|
||||
vo.setAllowDischarge(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (description.contains("待机")) {
|
||||
vo.setForceStandby(true);
|
||||
}
|
||||
if (description.contains("停机") || description.contains("切断") || level >= 3) {
|
||||
vo.setForceStop(true);
|
||||
vo.setForceStandby(true);
|
||||
vo.setAllowCharge(false);
|
||||
vo.setAllowDischarge(false);
|
||||
vo.setPowerLimitRatio(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
// 执行保护配置优先于描述文本配置
|
||||
List<ProtectionPlanVo> protectionPlans = parseProtectionPlan(plan.getProtectionPlan());
|
||||
applyCapabilityByProtectionPlan(vo, protectionPlans);
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
private void applyCapabilityByProtectionPlan(ProtectionConstraintVo vo, List<ProtectionPlanVo> protectionPlans) {
|
||||
if (CollectionUtils.isEmpty(protectionPlans)) {
|
||||
return;
|
||||
}
|
||||
for (ProtectionPlanVo item : protectionPlans) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
String marker = ((item.getPointName() == null ? "" : item.getPointName()) + " "
|
||||
+ (item.getPoint() == null ? "" : item.getPoint())).toLowerCase();
|
||||
if (StringUtils.isEmpty(marker)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (containsAny(marker, "降功率", "derate", "power_limit", "powerlimit")) {
|
||||
BigDecimal ratio = parseDerateRatioByPlan(item);
|
||||
if (ratio != null) {
|
||||
vo.setPowerLimitRatio(minRatio(vo.getPowerLimitRatio(), ratio));
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCapabilityEnabled(item.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (containsAny(marker, "禁止充放电", "forbid_charge_discharge", "disable_charge_discharge")) {
|
||||
vo.setAllowCharge(false);
|
||||
vo.setAllowDischarge(false);
|
||||
continue;
|
||||
}
|
||||
if (containsAny(marker, "禁止充电", "forbid_charge", "disable_charge")) {
|
||||
vo.setAllowCharge(false);
|
||||
}
|
||||
if (containsAny(marker, "禁止放电", "forbid_discharge", "disable_discharge")) {
|
||||
vo.setAllowDischarge(false);
|
||||
}
|
||||
if (containsAny(marker, "允许充电", "allow_charge")) {
|
||||
vo.setAllowCharge(true);
|
||||
}
|
||||
if (containsAny(marker, "允许放电", "allow_discharge")) {
|
||||
vo.setAllowDischarge(true);
|
||||
}
|
||||
if (containsAny(marker, "待机", "standby")) {
|
||||
vo.setForceStandby(true);
|
||||
}
|
||||
if (containsAny(marker, "关机", "停机", "切断", "shutdown", "stop")) {
|
||||
vo.setForceStop(true);
|
||||
vo.setForceStandby(true);
|
||||
vo.setAllowCharge(false);
|
||||
vo.setAllowDischarge(false);
|
||||
vo.setPowerLimitRatio(BigDecimal.ZERO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal parseDerateRatioByPlan(ProtectionPlanVo planVo) {
|
||||
BigDecimal value = planVo.getValue();
|
||||
if (value == null || value.compareTo(BigDecimal.ZERO) < 0) {
|
||||
return null;
|
||||
}
|
||||
if (value.compareTo(BigDecimal.ONE) <= 0) {
|
||||
return value;
|
||||
}
|
||||
if (value.compareTo(new BigDecimal("100")) <= 0) {
|
||||
return value.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isCapabilityEnabled(BigDecimal value) {
|
||||
return value == null || value.compareTo(BigDecimal.ZERO) != 0;
|
||||
}
|
||||
|
||||
private boolean containsAny(String text, String... markers) {
|
||||
if (StringUtils.isEmpty(text) || markers == null) {
|
||||
return false;
|
||||
}
|
||||
for (String marker : markers) {
|
||||
if (!StringUtils.isEmpty(marker) && text.contains(marker)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private BigDecimal parseDerateRatio(String text) {
|
||||
if (StringUtils.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
Matcher m = Pattern.compile("降功率\\s*(\\d+(?:\\.\\d+)?)%")
|
||||
.matcher(text);
|
||||
if (!m.find()) {
|
||||
return null;
|
||||
}
|
||||
BigDecimal percent = new BigDecimal(m.group(1));
|
||||
if (percent.compareTo(BigDecimal.ZERO) < 0) {
|
||||
return null;
|
||||
}
|
||||
return percent.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private void mergeConstraint(ProtectionConstraintVo merged, ProtectionConstraintVo incoming) {
|
||||
if (incoming == null) {
|
||||
return;
|
||||
}
|
||||
merged.setLevel(Math.max(nullSafeInt(merged.getLevel()), nullSafeInt(incoming.getLevel())));
|
||||
merged.setAllowCharge(boolAnd(merged.getAllowCharge(), incoming.getAllowCharge()));
|
||||
merged.setAllowDischarge(boolAnd(merged.getAllowDischarge(), incoming.getAllowDischarge()));
|
||||
merged.setForceStandby(boolOr(merged.getForceStandby(), incoming.getForceStandby()));
|
||||
merged.setForceStop(boolOr(merged.getForceStop(), incoming.getForceStop()));
|
||||
merged.setPowerLimitRatio(minRatio(merged.getPowerLimitRatio(), incoming.getPowerLimitRatio()));
|
||||
|
||||
if (incoming.getSourcePlanIds() != null && !incoming.getSourcePlanIds().isEmpty()) {
|
||||
if (merged.getSourcePlanIds() == null) {
|
||||
merged.setSourcePlanIds(new ArrayList<>());
|
||||
}
|
||||
merged.getSourcePlanIds().addAll(incoming.getSourcePlanIds());
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal minRatio(BigDecimal a, BigDecimal b) {
|
||||
BigDecimal left = a == null ? BigDecimal.ONE : a;
|
||||
BigDecimal right = b == null ? BigDecimal.ONE : b;
|
||||
return left.min(right);
|
||||
}
|
||||
|
||||
private int nullSafeInt(Integer value) {
|
||||
return value == null ? 0 : value;
|
||||
}
|
||||
|
||||
private Boolean boolAnd(Boolean a, Boolean b) {
|
||||
boolean left = a == null || a;
|
||||
boolean right = b == null || b;
|
||||
return left && right;
|
||||
}
|
||||
|
||||
private Boolean boolOr(Boolean a, Boolean b) {
|
||||
boolean left = a != null && a;
|
||||
boolean right = b != null && b;
|
||||
return left || right;
|
||||
}
|
||||
|
||||
private boolean checkIsNeedCancelPlan(List<ProtectionSettingVo> protSettings, String siteId) {
|
||||
StringBuilder conditionSb = new StringBuilder();
|
||||
for (int i = 0; i < protSettings.size(); i++) {
|
||||
ProtectionSettingVo vo = protSettings.get(i);
|
||||
String deviceId = vo.getDeviceId();
|
||||
String point = vo.getPoint();
|
||||
BigDecimal releaseValue = vo.getReleaseValue();
|
||||
if (StringUtils.isEmpty(deviceId)
|
||||
|| StringUtils.isEmpty(point)
|
||||
|| releaseValue == null
|
||||
|| StringUtils.isEmpty(vo.getReleaseOperator())) {
|
||||
return false;
|
||||
}
|
||||
BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
|
||||
if (lastPointValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
conditionSb.append(lastPointValue).append(vo.getReleaseOperator()).append(releaseValue);
|
||||
if (i < protSettings.size() - 1) {
|
||||
conditionSb.append(" ").append(vo.getRelationNext()).append(" ");
|
||||
}
|
||||
}
|
||||
return executeWithParser(conditionSb.toString());
|
||||
}
|
||||
|
||||
private boolean checkIsNeedIssuedPlan(List<ProtectionSettingVo> protSettings, String siteId) {
|
||||
StringBuilder conditionSb = new StringBuilder();
|
||||
for (int i = 0; i < protSettings.size(); i++) {
|
||||
ProtectionSettingVo vo = protSettings.get(i);
|
||||
String deviceId = vo.getDeviceId();
|
||||
String point = vo.getPoint();
|
||||
BigDecimal faultValue = vo.getFaultValue();
|
||||
if (StringUtils.isEmpty(deviceId)
|
||||
|| StringUtils.isEmpty(point)
|
||||
|| faultValue == null
|
||||
|| StringUtils.isEmpty(vo.getFaultOperator())) {
|
||||
return false;
|
||||
}
|
||||
BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
|
||||
if (lastPointValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
conditionSb.append(lastPointValue).append(vo.getFaultOperator()).append(faultValue);
|
||||
if (i < protSettings.size() - 1) {
|
||||
conditionSb.append(" ").append(vo.getRelationNext()).append(" ");
|
||||
}
|
||||
}
|
||||
return executeWithParser(conditionSb.toString());
|
||||
}
|
||||
|
||||
private BigDecimal getPointLastValue(String deviceId, String point, String siteId) {
|
||||
JSONObject mqttJson = redisCache.getCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceId);
|
||||
if (mqttJson == null) {
|
||||
return null;
|
||||
}
|
||||
String jsonData = mqttJson.get("Data").toString();
|
||||
if (StringUtils.isEmpty(jsonData)) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> obj = JSON.parseObject(jsonData, new com.alibaba.fastjson2.TypeReference<Map<String, Object>>() {});
|
||||
return StringUtils.getBigDecimal(obj.get(point));
|
||||
}
|
||||
|
||||
private EmsAlarmRecords addAlarmRecord(String siteId, String content, String level) {
|
||||
EmsAlarmRecords emsAlarmRecords = new EmsAlarmRecords();
|
||||
emsAlarmRecords.setSiteId(siteId);
|
||||
emsAlarmRecords.setAlarmContent(content);
|
||||
emsAlarmRecords.setAlarmLevel(level);
|
||||
emsAlarmRecords.setAlarmStartTime(new Date());
|
||||
emsAlarmRecords.setStatus(AlarmStatus.WAITING.getCode());
|
||||
emsAlarmRecords.setDeviceType("TCP");
|
||||
emsAlarmRecords.setCreateBy("system");
|
||||
emsAlarmRecords.setCreateTime(new Date());
|
||||
return emsAlarmRecords;
|
||||
}
|
||||
|
||||
private String getAlarmLevel(Integer faultLevel) {
|
||||
if (ObjectUtils.isEmpty(faultLevel) || faultLevel < 1 || faultLevel > 3) {
|
||||
logger.warn("非法故障等级:{},默认返回紧急告警", faultLevel);
|
||||
return AlarmLevelStatus.EMERGENCY.getCode();
|
||||
}
|
||||
switch (faultLevel) {
|
||||
case 1:
|
||||
return AlarmLevelStatus.GENERAL.getCode();
|
||||
case 2:
|
||||
return AlarmLevelStatus.SERIOUS.getCode();
|
||||
case 3:
|
||||
return AlarmLevelStatus.EMERGENCY.getCode();
|
||||
default:
|
||||
return AlarmLevelStatus.EMERGENCY.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义表达式解析器(仅支持简单运算符和逻辑关系)
|
||||
*/
|
||||
public boolean executeWithParser(String conditionStr) {
|
||||
if (conditionStr == null || conditionStr.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> logicRelations = new ArrayList<>();
|
||||
Pattern logicPattern = Pattern.compile("(&&|\\|\\|)");
|
||||
Matcher logicMatcher = logicPattern.matcher(conditionStr);
|
||||
while (logicMatcher.find()) {
|
||||
logicRelations.add(logicMatcher.group());
|
||||
}
|
||||
|
||||
String[] atomicConditions = logicPattern.split(conditionStr);
|
||||
List<Boolean> atomicResults = new ArrayList<>();
|
||||
Pattern conditionPattern = Pattern.compile("(\\d+\\.?\\d*)\\s*([><]=?|==)\\s*(\\d+\\.?\\d*)");
|
||||
for (String atomic : atomicConditions) {
|
||||
Matcher matcher = conditionPattern.matcher(atomic.trim());
|
||||
if (!matcher.matches()) {
|
||||
logger.error("无效的原子条件:{}", atomic);
|
||||
return false;
|
||||
}
|
||||
double left = Double.parseDouble(matcher.group(1));
|
||||
String operator = matcher.group(2);
|
||||
double right = Double.parseDouble(matcher.group(3));
|
||||
|
||||
boolean result;
|
||||
switch (operator) {
|
||||
case ">":
|
||||
result = left > right;
|
||||
break;
|
||||
case ">=":
|
||||
result = left >= right;
|
||||
break;
|
||||
case "<":
|
||||
result = left < right;
|
||||
break;
|
||||
case "<=":
|
||||
result = left <= right;
|
||||
break;
|
||||
case "==":
|
||||
result = left == right;
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
atomicResults.add(result);
|
||||
}
|
||||
|
||||
boolean finalResult = atomicResults.get(0);
|
||||
for (int i = 0; i < logicRelations.size(); i++) {
|
||||
String relation = logicRelations.get(i);
|
||||
boolean nextResult = atomicResults.get(i + 1);
|
||||
if ("&&".equals(relation)) {
|
||||
finalResult = finalResult && nextResult;
|
||||
} else if ("||".equals(relation)) {
|
||||
finalResult = finalResult || nextResult;
|
||||
}
|
||||
}
|
||||
return finalResult;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,776 @@
|
||||
package com.xzzn.quartz.task;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.xzzn.common.constant.RedisKeyConstants;
|
||||
import com.xzzn.common.core.modbus.ModbusProcessor;
|
||||
import com.xzzn.common.core.modbus.domain.DeviceConfig;
|
||||
import com.xzzn.common.core.modbus.domain.WriteTagConfig;
|
||||
import com.xzzn.common.core.redis.RedisCache;
|
||||
import com.xzzn.common.enums.BusinessStatus;
|
||||
import com.xzzn.common.enums.BusinessType;
|
||||
import com.xzzn.common.enums.ChargeStatus;
|
||||
import com.xzzn.common.enums.DeviceCategory;
|
||||
import com.xzzn.common.enums.OperatorType;
|
||||
import com.xzzn.common.enums.SiteDevice;
|
||||
import com.xzzn.common.enums.SocLimit;
|
||||
import com.xzzn.common.enums.WorkStatus;
|
||||
import com.xzzn.common.utils.DateUtils;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsAmmeterData;
|
||||
import com.xzzn.ems.domain.EmsBatteryStack;
|
||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
||||
import com.xzzn.ems.domain.EmsPcsSetting;
|
||||
import com.xzzn.ems.domain.EmsStrategyRuntimeConfig;
|
||||
import com.xzzn.ems.domain.EmsStrategyLog;
|
||||
import com.xzzn.ems.domain.EmsStrategyTemp;
|
||||
import com.xzzn.ems.domain.EmsStrategyTimeConfig;
|
||||
import com.xzzn.ems.domain.vo.ProtectionConstraintVo;
|
||||
import com.xzzn.ems.domain.vo.StrategyRunningVo;
|
||||
import com.xzzn.ems.mapper.EmsAmmeterDataMapper;
|
||||
import com.xzzn.ems.mapper.EmsBatteryStackMapper;
|
||||
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
||||
import com.xzzn.ems.mapper.EmsPcsSettingMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyLogMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyRuntimeConfigMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyTempMapper;
|
||||
import com.xzzn.ems.mapper.EmsStrategyTimeConfigMapper;
|
||||
import com.xzzn.system.domain.SysOperLog;
|
||||
import com.xzzn.system.service.ISysOperLogService;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("strategyPoller")
|
||||
public class StrategyPoller {
|
||||
private static final Logger logger = LoggerFactory.getLogger(StrategyPoller.class);
|
||||
|
||||
private static final ConcurrentHashMap<Long, Boolean> strategyLocks = new ConcurrentHashMap<>();
|
||||
|
||||
// SOC 上下限值,默认为0%-100%
|
||||
private static final BigDecimal DEFAULT_SOC_DOWN = BigDecimal.ZERO;
|
||||
private static final BigDecimal DEFAULT_SOC_UP = new BigDecimal(100);
|
||||
// 逆变器功率下限值,默认为30kW
|
||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_THRESHOLD = new BigDecimal(30);
|
||||
// 逆变器下限值范围,默认为20%
|
||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_RANGE_PERCENT = new BigDecimal(20);
|
||||
// 逆变器功率上限值,默认为100kW
|
||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_UP = new BigDecimal(100);
|
||||
// PCS功率降幅,默认为10%
|
||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT = new BigDecimal(10);
|
||||
// 电网有功功率低于20kW时,强制待机
|
||||
private static final BigDecimal DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD = new BigDecimal(20);
|
||||
// 设定功率倍率,默认10
|
||||
private static final BigDecimal DEFAULT_POWER_SET_MULTIPLIER = new BigDecimal(10);
|
||||
// 保护介入默认开启
|
||||
private static final Integer DEFAULT_PROTECT_INTERVENE_ENABLE = 1;
|
||||
// 一级保护默认降额50%
|
||||
private static final BigDecimal DEFAULT_PROTECT_L1_DERATE_PERCENT = new BigDecimal("50");
|
||||
// 保护约束失效保护时长(秒)
|
||||
private static final Integer DEFAULT_PROTECT_RECOVERY_STABLE_SECONDS = 5;
|
||||
// 三级保护默认锁存开启
|
||||
private static final Integer DEFAULT_PROTECT_L3_LATCH_ENABLE = 1;
|
||||
// 保护冲突策略默认值
|
||||
private static final String DEFAULT_PROTECT_CONFLICT_POLICY = "MAX_LEVEL_WIN";
|
||||
// 保护约束默认功率比例
|
||||
private static final BigDecimal DEFAULT_PROTECTION_RATIO = BigDecimal.ONE;
|
||||
// 除法精度,避免BigDecimal除不尽异常
|
||||
private static final int POWER_SCALE = 4;
|
||||
|
||||
@Autowired
|
||||
private EmsStrategyRunningMapper emsStrategyRunningMapper;
|
||||
@Autowired
|
||||
private EmsStrategyTempMapper emsStrategyTempMapper;
|
||||
@Autowired
|
||||
private EmsStrategyTimeConfigMapper emsStrategyTimeConfigMapper;
|
||||
@Autowired
|
||||
private EmsBatteryStackMapper emsBatteryStackMapper;
|
||||
@Autowired
|
||||
private EmsDevicesSettingMapper emsDevicesMapper;
|
||||
@Autowired
|
||||
private EmsPcsSettingMapper emsPcsSettingMapper;
|
||||
@Autowired
|
||||
private EmsAmmeterDataMapper emsAmmeterDataMapper;
|
||||
@Autowired
|
||||
private EmsStrategyLogMapper emsStrategyLogMapper;
|
||||
@Autowired
|
||||
private EmsStrategyRuntimeConfigMapper runtimeConfigMapper;
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
@Autowired
|
||||
private ModbusProcessor modbusProcessor;
|
||||
@Autowired
|
||||
private ISysOperLogService operLogService;
|
||||
|
||||
@Resource(name = "modbusExecutor")
|
||||
private ExecutorService modbusExecutor;
|
||||
|
||||
public void pollAllDevices() {
|
||||
logger.info("开始执行运行策略数据轮询...");
|
||||
List<StrategyRunningVo> strategyRunningVoList = emsStrategyRunningMapper.getPendingPollerStrategy(null);
|
||||
strategyRunningVoList.forEach(strategyVo -> {
|
||||
Long strategyId = strategyVo.getId();
|
||||
if (strategyLocks.putIfAbsent(strategyId, true) == null) {
|
||||
// 使用共享的modbusExecutor串行执行,避免与ModbusPoller并发访问导致通讯故障
|
||||
modbusExecutor.submit(() -> {
|
||||
try {
|
||||
processData(strategyVo);
|
||||
} catch (Exception e) {
|
||||
logger.error("运行策略{}轮询异常", strategyVo.getId(), e);
|
||||
} finally {
|
||||
logger.info("运行策略{}轮询任务执行完成,释放锁", strategyVo.getId());
|
||||
strategyLocks.remove(strategyId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
logger.info("策略{}已在处理中,跳过重复执行", strategyId);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 处理获取到的运行策略数据,modbus发送指定的命令控制设备
|
||||
private void processData(StrategyRunningVo strategyVo) {
|
||||
logger.info("运行策略数据处理开始");
|
||||
// 根据运行策略获取主副策略的模板数据
|
||||
Long mainStrategyId = strategyVo.getMainStrategyId();
|
||||
Long auxStrategyId = strategyVo.getAuxStrategyId();
|
||||
String siteId = strategyVo.getSiteId();
|
||||
// 处理主策略数据
|
||||
if (mainStrategyId != null && StringUtils.isNotBlank(siteId)) {
|
||||
dealStrategyCurveData(mainStrategyId, siteId);
|
||||
}
|
||||
// 处理副策略数据
|
||||
if (auxStrategyId != null && StringUtils.isNotBlank(siteId)) {
|
||||
dealStrategyCurveData(auxStrategyId, siteId);
|
||||
}
|
||||
|
||||
logger.info("运行策略轮询处理结束");
|
||||
}
|
||||
|
||||
private void dealStrategyCurveData(Long strategyId, String siteId) {
|
||||
EmsStrategyRuntimeConfig runtimeConfig = getRuntimeConfig(siteId);
|
||||
// 1.获取当前策略的所有模板
|
||||
List<Map<String, String>> temps = emsStrategyTempMapper.getTempNameList(strategyId, siteId);
|
||||
if (CollectionUtils.isEmpty(temps)) {
|
||||
logger.info("当前站点: {}, 策略: {} 没有模板数据", siteId, strategyId);
|
||||
return;
|
||||
}
|
||||
for (Map<String, String> temp : temps) {
|
||||
// 2.查询当月配置的运行策略
|
||||
String tempId = temp.get("templateId");
|
||||
int month = LocalDateTime.now().getMonthValue();
|
||||
List<EmsStrategyTimeConfig> timeConfigs = emsStrategyTimeConfigMapper.getTimeConfigByTempIdAndMonth(tempId, month);
|
||||
if (CollectionUtils.isEmpty(timeConfigs)) {
|
||||
continue;
|
||||
}
|
||||
logger.info("当前站点: {}, 策略: {}, {}月配置模版:{}", siteId, strategyId, month, tempId);
|
||||
// 3.查询当月配置的运行策略时间阶段数据
|
||||
List<EmsStrategyTemp> powerConfig = emsStrategyTempMapper.selectStrategyTempByTempId(tempId);
|
||||
if (CollectionUtils.isEmpty(powerConfig)) {
|
||||
logger.info("当前站点: {}, 策略: {}, 模版:{} 未配置数据", siteId, strategyId, tempId);
|
||||
continue;
|
||||
}
|
||||
// 4.遍历时间段数据,判断当前时间是否在时间段内,在时间段内的进行处理
|
||||
for (EmsStrategyTemp emsStrategyTemp : powerConfig) {
|
||||
if (emsStrategyTemp.getStartTime() == null || emsStrategyTemp.getEndTime() == null) {
|
||||
logger.info("当前站点: {}, 策略: {}, 模版:{} 未配置时间阶段数据", siteId, strategyId, tempId);
|
||||
continue;
|
||||
}
|
||||
// 判断当前时间是否在时间段内
|
||||
if (!isTimeInRange(LocalTime.now(), emsStrategyTemp.getStartTime(), emsStrategyTemp.getEndTime())) {
|
||||
logger.info("当前站点: {}, 策略: {}, 时间段:{} 不在时间段内", siteId, strategyId, emsStrategyTemp.getStartTime() + "-" + emsStrategyTemp.getEndTime());
|
||||
continue;
|
||||
}
|
||||
// 查询PCS设备信息
|
||||
EmsDevicesSetting queryDevices = new EmsDevicesSetting();
|
||||
queryDevices.setSiteId(siteId);
|
||||
queryDevices.setDeviceCategory(DeviceCategory.PCS.getCode());
|
||||
List<EmsDevicesSetting> pcsDeviceList = emsDevicesMapper.selectEmsDevicesSettingList(queryDevices);
|
||||
if (CollectionUtils.isEmpty(pcsDeviceList)) {
|
||||
logger.info("当前站点: {} 未配置PCS设备", siteId);
|
||||
continue;
|
||||
}
|
||||
// 判断SOC上下限
|
||||
if (isSocInRange(emsStrategyTemp, runtimeConfig)) {
|
||||
ProtectionConstraintVo protectionConstraint = getProtectionConstraint(siteId);
|
||||
Map<Long, EmsPcsSetting> pcsSettingCache = new HashMap<>();
|
||||
BigDecimal avgChargeDischargePower = emsStrategyTemp.getChargeDischargePower()
|
||||
.divide(new BigDecimal(pcsDeviceList.size()), POWER_SCALE, RoundingMode.HALF_UP);
|
||||
BigDecimal totalActivePower = null;
|
||||
if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
|
||||
// 同一站点同一轮执行只读取一次电网电表,降低重复查库和数据抖动
|
||||
EmsAmmeterData emsAmmeterData = emsAmmeterDataMapper.getLastData(emsStrategyTemp.getSiteId(), SiteDevice.LOAD.name());
|
||||
if (emsAmmeterData != null) {
|
||||
totalActivePower = emsAmmeterData.getTotalActivePower();
|
||||
}
|
||||
}
|
||||
for (EmsDevicesSetting pcsDevice : pcsDeviceList) {
|
||||
EmsPcsSetting pcsSetting = pcsSettingCache.computeIfAbsent(
|
||||
pcsDevice.getId(),
|
||||
id -> emsPcsSettingMapper.selectEmsPcsSettingByDeviceId(id)
|
||||
);
|
||||
if (pcsSetting == null || pcsSetting.getClusterNum() < 1) {
|
||||
logger.info("当前站点: {}, PCS设备: {} 未获取电池簇数量", siteId, pcsDevice.getDeviceId());
|
||||
continue;
|
||||
}
|
||||
// 平均功率值根据倍率放大后,再按电池簇数量平均分配
|
||||
BigDecimal strategyPower = avgChargeDischargePower.multiply(runtimeConfig.getPowerSetMultiplier())
|
||||
.divide(new BigDecimal(pcsSetting.getClusterNum()), POWER_SCALE, RoundingMode.HALF_UP);
|
||||
// 根据充电状态,处理数据
|
||||
if (ChargeStatus.CHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
|
||||
StrategyCommandDecision decision = applyProtectionConstraint(
|
||||
strategyPower,
|
||||
ChargeStatus.CHARGING,
|
||||
runtimeConfig,
|
||||
protectionConstraint
|
||||
);
|
||||
// 发送Modbus命令控制设备-充电
|
||||
sendModbusCommand(
|
||||
Collections.singletonList(pcsDevice),
|
||||
pcsSetting,
|
||||
decision.getChargeStatus(),
|
||||
decision.getPower(),
|
||||
emsStrategyTemp,
|
||||
false,
|
||||
null
|
||||
);
|
||||
} else if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus())) {
|
||||
boolean needAntiReverseFlow = false;
|
||||
Integer powerDownType = null;
|
||||
BigDecimal chargeDischargePower = strategyPower;
|
||||
// 查询策略运行日志
|
||||
EmsStrategyLog lastStrategyLog = getLastStrategyLog(pcsDevice.getDeviceId(), emsStrategyTemp);
|
||||
if (lastStrategyLog != null) {
|
||||
// 如果当前时间段已经进入待机状态,则不处理
|
||||
if (ChargeStatus.STANDBY.getCode().equals(lastStrategyLog.getChargeStatus())) {
|
||||
continue;
|
||||
}
|
||||
chargeDischargePower = lastStrategyLog.getChargeDischargePower();
|
||||
powerDownType = lastStrategyLog.getPowerDownType();
|
||||
}
|
||||
|
||||
// 查询电网电表的正向有功功率,36kW-50kW范围内,稳定运行,低于36kW,降功率,高于50kW,增加功率
|
||||
if (totalActivePower == null) {
|
||||
logger.warn("当前站点: {}, 未获取到最新电表数据,执行保守策略并切换待机", emsStrategyTemp.getSiteId());
|
||||
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, true, 0);
|
||||
continue;
|
||||
} else {
|
||||
// 电网功率过低,直接待机,不再放电
|
||||
if (totalActivePower.compareTo(runtimeConfig.getAntiReverseHardStopThreshold()) < 0) {
|
||||
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, true, 0);
|
||||
continue;
|
||||
}
|
||||
// 放电开始先按差值限幅:差值=电网功率-防逆流阈值
|
||||
BigDecimal diffPower = totalActivePower.subtract(runtimeConfig.getAntiReverseThreshold());
|
||||
BigDecimal targetPower = diffPower.compareTo(BigDecimal.ZERO) > 0 ? diffPower : BigDecimal.ZERO;
|
||||
if (targetPower.compareTo(strategyPower) > 0) {
|
||||
targetPower = strategyPower;
|
||||
}
|
||||
if (chargeDischargePower.compareTo(targetPower) > 0) {
|
||||
chargeDischargePower = targetPower;
|
||||
}
|
||||
// 判断是否需要防逆流
|
||||
needAntiReverseFlow = isNeedAntiReverseFlow(totalActivePower, runtimeConfig);
|
||||
BigDecimal power = strategyPower.multiply(runtimeConfig.getAntiReversePowerDownPercent())
|
||||
.divide(new BigDecimal(100), POWER_SCALE, RoundingMode.HALF_UP);
|
||||
if (needAntiReverseFlow) {
|
||||
// 降功率
|
||||
chargeDischargePower = chargeDischargePower.subtract(power);
|
||||
powerDownType = 0;
|
||||
} else {
|
||||
// 判断是否需要增加功率,
|
||||
if (powerDownType != null && totalActivePower.compareTo(runtimeConfig.getAntiReverseUp()) > 0) {
|
||||
if (chargeDischargePower.compareTo(targetPower) >= 0) {
|
||||
// 功率增加到限幅值则停止
|
||||
continue;
|
||||
}
|
||||
// 增加功率
|
||||
chargeDischargePower = chargeDischargePower.add(power);
|
||||
if (chargeDischargePower.compareTo(targetPower) > 0) {
|
||||
chargeDischargePower = targetPower;
|
||||
}
|
||||
powerDownType = 1;
|
||||
needAntiReverseFlow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chargeDischargePower.compareTo(BigDecimal.ZERO) < 0) {
|
||||
chargeDischargePower = BigDecimal.ZERO;
|
||||
}
|
||||
StrategyCommandDecision decision = applyProtectionConstraint(
|
||||
chargeDischargePower,
|
||||
ChargeStatus.DISCHARGING,
|
||||
runtimeConfig,
|
||||
protectionConstraint
|
||||
);
|
||||
ChargeStatus finalStatus = decision.getChargeStatus();
|
||||
BigDecimal finalPower = decision.getPower();
|
||||
if (ChargeStatus.STANDBY.equals(finalStatus) || BigDecimal.ZERO.compareTo(finalPower) == 0) {
|
||||
// 如果已经降功率到0,则设备直接待机
|
||||
// 发送Modbus命令控制设备-待机
|
||||
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, needAntiReverseFlow, powerDownType);
|
||||
} else {
|
||||
// 发送Modbus命令控制设备-放电
|
||||
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, finalStatus, finalPower, emsStrategyTemp, needAntiReverseFlow, powerDownType);
|
||||
}
|
||||
} else {
|
||||
// 发送Modbus命令控制设备-待机
|
||||
sendModbusCommand(Collections.singletonList(pcsDevice), pcsSetting, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, false, null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 发送Modbus命令控制设备-待机
|
||||
sendModbusCommand(pcsDeviceList, null, ChargeStatus.STANDBY, BigDecimal.ZERO, emsStrategyTemp, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void saveStrategyLog(String deviceId, BigDecimal chargeDischargePower, String chargeStatus,
|
||||
EmsStrategyTemp strategyTemp, boolean needAntiReverseFlow, Integer powerDownType) {
|
||||
EmsStrategyLog log = new EmsStrategyLog();
|
||||
log.setStrategyId(strategyTemp.getStrategyId());
|
||||
log.setTemplateId(strategyTemp.getTemplateId());
|
||||
log.setSiteId(strategyTemp.getSiteId());
|
||||
log.setDeviceId(deviceId);
|
||||
log.setStartTime(strategyTemp.getStartTime());
|
||||
log.setEndTime(strategyTemp.getEndTime());
|
||||
log.setChargeDischargePower(chargeDischargePower);
|
||||
log.setChargeStatus(chargeStatus);
|
||||
log.setExecutionDate(DateUtils.toDate(LocalDateTime.now()));
|
||||
log.setAntiReverse(needAntiReverseFlow ? 1 : 0);
|
||||
log.setPowerDownType(powerDownType);
|
||||
emsStrategyLogMapper.insertEmsStrategyLog(log);
|
||||
}
|
||||
|
||||
private List<EmsStrategyLog> getStrategyLog(String deviceId, String chargeStatus,
|
||||
EmsStrategyTemp strategyTemp, boolean needAntiReverseFlow) {
|
||||
EmsStrategyLog query = new EmsStrategyLog();
|
||||
query.setStrategyId(strategyTemp.getStrategyId());
|
||||
query.setTemplateId(strategyTemp.getTemplateId());
|
||||
query.setSiteId(strategyTemp.getSiteId());
|
||||
query.setDeviceId(deviceId);
|
||||
query.setStartTime(strategyTemp.getStartTime());
|
||||
query.setEndTime(strategyTemp.getEndTime());
|
||||
query.setChargeStatus(chargeStatus);
|
||||
query.setExecutionDate(DateUtils.toDate(LocalDateTime.now()));
|
||||
query.setAntiReverse(needAntiReverseFlow ? 1 : 0);
|
||||
return emsStrategyLogMapper.selectEmsStrategyLogList(query);
|
||||
}
|
||||
|
||||
private EmsStrategyLog getLastStrategyLog(String deviceId, EmsStrategyTemp strategyTemp) {
|
||||
EmsStrategyLog query = new EmsStrategyLog();
|
||||
query.setStrategyId(strategyTemp.getStrategyId());
|
||||
query.setTemplateId(strategyTemp.getTemplateId());
|
||||
query.setSiteId(strategyTemp.getSiteId());
|
||||
query.setDeviceId(deviceId);
|
||||
query.setStartTime(strategyTemp.getStartTime());
|
||||
query.setEndTime(strategyTemp.getEndTime());
|
||||
query.setExecutionDate(DateUtils.toDate(LocalDateTime.now()));
|
||||
return emsStrategyLogMapper.getLastStrategyLog(query);
|
||||
}
|
||||
|
||||
private boolean isNeedAntiReverseFlow(BigDecimal totalActivePower, EmsStrategyRuntimeConfig runtimeConfig) {
|
||||
// 获取当前设定的防逆流阈值(30kW)
|
||||
BigDecimal threshold = runtimeConfig.getAntiReverseThreshold();
|
||||
// 计算20%范围的上限(36kW)
|
||||
BigDecimal upperLimit = threshold.multiply(runtimeConfig.getAntiReverseRangePercent()).divide(new BigDecimal(100)).add(threshold);
|
||||
|
||||
// 判断电网电表正向有功功率是否小于36kW(接近30kW的20%范围)
|
||||
return totalActivePower.compareTo(upperLimit) < 0;
|
||||
}
|
||||
|
||||
public List<WriteTagConfig> getSwitchDeviceWriteTags(EmsPcsSetting pcsSetting, String workStatus) {
|
||||
List<WriteTagConfig> writeTags = new ArrayList<>();
|
||||
BigDecimal power;
|
||||
|
||||
if (WorkStatus.NORMAL.getCode().equals(workStatus)) {
|
||||
// 开机先发送开机指令再发送有功功率给定值
|
||||
WriteTagConfig writeTag = new WriteTagConfig();
|
||||
writeTag.setAddress(pcsSetting.getPointAddress());
|
||||
writeTag.setValue(pcsSetting.getStartCommand());
|
||||
writeTags.add(writeTag);
|
||||
|
||||
power = pcsSetting.getStartPower();
|
||||
} else {
|
||||
// 关机
|
||||
power = pcsSetting.getStopPower();
|
||||
}
|
||||
|
||||
JSONArray array = JSON.parseArray(pcsSetting.getClusterPointAddress());
|
||||
for (int i = 0; i < pcsSetting.getClusterNum(); i++) {
|
||||
Object clusterPointAddress = array.get(i);
|
||||
WriteTagConfig clusterWriteTag = new WriteTagConfig();
|
||||
clusterWriteTag.setAddress(String.valueOf(clusterPointAddress));
|
||||
// 电池簇PCS有功功率给定默认置0
|
||||
if (power == null) {
|
||||
power = BigDecimal.ZERO;
|
||||
}
|
||||
clusterWriteTag.setValue(power);
|
||||
writeTags.add(clusterWriteTag);
|
||||
}
|
||||
if (WorkStatus.STOP.getCode().equals(workStatus)) {
|
||||
// 关机先发送有功功率给定值再发送关机指令
|
||||
WriteTagConfig writeTag = new WriteTagConfig();
|
||||
writeTag.setAddress(pcsSetting.getPointAddress());
|
||||
writeTag.setValue(pcsSetting.getStopCommand());
|
||||
writeTags.add(writeTag);
|
||||
}
|
||||
return writeTags;
|
||||
}
|
||||
|
||||
public List<WriteTagConfig> getWriteTags(EmsPcsSetting pcsSetting, BigDecimal chargeDischargePower) {
|
||||
List<WriteTagConfig> writeTags = new ArrayList<>();
|
||||
JSONArray array = JSON.parseArray(pcsSetting.getClusterPointAddress());
|
||||
for (int i = 0; i < pcsSetting.getClusterNum(); i++) {
|
||||
Object clusterPointAddress = array.get(i);
|
||||
WriteTagConfig clusterWriteTag = new WriteTagConfig();
|
||||
clusterWriteTag.setAddress(String.valueOf(clusterPointAddress));
|
||||
clusterWriteTag.setValue(chargeDischargePower);
|
||||
writeTags.add(clusterWriteTag);
|
||||
}
|
||||
return writeTags;
|
||||
}
|
||||
|
||||
public DeviceConfig getDeviceConfig(String siteId, String deviceId, EmsDevicesSetting device, EmsPcsSetting pcsSetting, BigDecimal chargeDischargePower, int writeType) {
|
||||
if (Objects.isNull(pcsSetting)) {
|
||||
pcsSetting = emsPcsSettingMapper.selectEmsPcsSettingByDeviceId(device.getId());
|
||||
if (pcsSetting == null) {
|
||||
logger.info("当前站点: {}, PCS设备: {} 未找到对应PCS配置", siteId, deviceId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (device.getIpPort() == null || device.getSlaveId() == null) {
|
||||
logger.info("当前站点: {}, PCS设备: {} 未配置IP端口或从站号", siteId, deviceId);
|
||||
return null;
|
||||
}
|
||||
DeviceConfig deviceConfig = new DeviceConfig();
|
||||
deviceConfig.setDeviceNumber(device.getDeviceId());
|
||||
deviceConfig.setDeviceName(device.getDeviceName());
|
||||
deviceConfig.setSlaveId(device.getSlaveId().intValue());
|
||||
deviceConfig.setHost(device.getIpAddress());
|
||||
deviceConfig.setPort(device.getIpPort().intValue());
|
||||
deviceConfig.setWriteTags(writeType == 0 ? getWriteTags(pcsSetting, chargeDischargePower) : getSwitchDeviceWriteTags(pcsSetting, device.getWorkStatus()));
|
||||
return deviceConfig;
|
||||
}
|
||||
|
||||
private void sendModbusCommand(List<EmsDevicesSetting> pcsDeviceList, EmsPcsSetting pcsSetting, ChargeStatus chargeStatus, BigDecimal chargeDischargePower,
|
||||
EmsStrategyTemp emsStrategyTemp, boolean needAntiReverseFlow, Integer powerDownType) {
|
||||
for (EmsDevicesSetting pcsDevice : pcsDeviceList) {
|
||||
String siteId = pcsDevice.getSiteId();
|
||||
String deviceId = pcsDevice.getDeviceId();
|
||||
List<EmsStrategyLog> strategyLogList = getStrategyLog(deviceId, chargeStatus.getCode(), emsStrategyTemp, needAntiReverseFlow);
|
||||
if (CollectionUtils.isNotEmpty(strategyLogList)) {
|
||||
boolean isExist = true;
|
||||
if (ChargeStatus.DISCHARGING.equals(chargeStatus) && needAntiReverseFlow) {
|
||||
isExist = false;
|
||||
}
|
||||
if (isExist) {
|
||||
logger.info("当前站点: {}, PCS设备: {} 当前时间段已存在策略执行记录,不再重复执行", siteId, deviceId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 每次操作先判断设备工作状态
|
||||
if (StringUtils.isEmpty(pcsDevice.getWorkStatus()) || WorkStatus.ABNORMAL.getCode().equals(pcsDevice.getWorkStatus())) {
|
||||
// 设备故障,不发送指令
|
||||
continue;
|
||||
} else if (WorkStatus.STOP.getCode().equals(pcsDevice.getWorkStatus())) {
|
||||
// 设备停机
|
||||
if (ChargeStatus.STANDBY.equals(chargeStatus)) {
|
||||
// 待机,则不写入功率值
|
||||
continue;
|
||||
} else {
|
||||
// 充、放电,则先开机设备
|
||||
if (!switchDevice(pcsDevice, pcsSetting, WorkStatus.NORMAL)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeviceConfig deviceConfig = getDeviceConfig(siteId, deviceId, pcsDevice, pcsSetting, chargeDischargePower, 0);
|
||||
if (deviceConfig == null) {
|
||||
continue;
|
||||
}
|
||||
boolean result = modbusProcessor.writeDataToDeviceWithRetry(deviceConfig);
|
||||
if (!result) {
|
||||
logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, chargeStatus.getInfo());
|
||||
recordDeviceOperationLog(siteId, deviceId, "写功率", chargeDischargePower, false, chargeStatus.getInfo() + "功率下发失败");
|
||||
continue;
|
||||
} else {
|
||||
recordDeviceOperationLog(siteId, deviceId, "写功率", chargeDischargePower, true, null);
|
||||
if (ChargeStatus.STANDBY.equals(chargeStatus)) {
|
||||
// 待机,先写功率值,再关机
|
||||
if (!switchDevice(pcsDevice, pcsSetting, WorkStatus.STOP)) {
|
||||
logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, WorkStatus.STOP.getInfo());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 记录策略执行日志
|
||||
saveStrategyLog(deviceId, chargeDischargePower, chargeStatus.getCode(), emsStrategyTemp, needAntiReverseFlow, powerDownType);
|
||||
}
|
||||
}
|
||||
|
||||
//设备开关机
|
||||
private boolean switchDevice(EmsDevicesSetting pcsDevice, EmsPcsSetting pcsSetting, WorkStatus workStatus) {
|
||||
String siteId = pcsDevice.getSiteId();
|
||||
String deviceId = pcsDevice.getDeviceId();
|
||||
String originalWorkStatus = pcsDevice.getWorkStatus();
|
||||
pcsDevice.setWorkStatus(workStatus.getCode());
|
||||
DeviceConfig deviceConfig = getDeviceConfig(siteId, deviceId, pcsDevice, pcsSetting , null, 1);
|
||||
if (deviceConfig == null) {
|
||||
pcsDevice.setWorkStatus(originalWorkStatus);
|
||||
return false;
|
||||
}
|
||||
boolean result = modbusProcessor.writeDataToDeviceWithRetry(deviceConfig);
|
||||
if (!result) {
|
||||
pcsDevice.setWorkStatus(originalWorkStatus);
|
||||
logger.info("当前站点: {}, PCS设备: {} modbus控制设备{}指令发送失败", siteId, deviceId, workStatus.getInfo());
|
||||
recordDeviceOperationLog(siteId, deviceId, "开关机", workStatus.getInfo(), false, workStatus.getInfo() + "指令发送失败");
|
||||
} else {
|
||||
recordDeviceOperationLog(siteId, deviceId, "开关机", workStatus.getInfo(), true, null);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void recordDeviceOperationLog(String siteId, String deviceId, String action, Object param, boolean success, String errorMsg) {
|
||||
try {
|
||||
SysOperLog operLog = new SysOperLog();
|
||||
operLog.setTitle("策略设备控制-" + action);
|
||||
operLog.setBusinessType(BusinessType.UPDATE.ordinal());
|
||||
operLog.setMethod(this.getClass().getName() + "." + action);
|
||||
operLog.setRequestMethod("SCHEDULE");
|
||||
operLog.setOperatorType(OperatorType.OTHER.ordinal());
|
||||
operLog.setOperName("system");
|
||||
operLog.setOperIp("127.0.0.1");
|
||||
operLog.setOperUrl("/quartz/strategyPoller");
|
||||
operLog.setOperTime(DateUtils.getNowDate());
|
||||
Map<String, Object> operParam = new HashMap<>();
|
||||
operParam.put("siteId", siteId);
|
||||
operParam.put("deviceId", deviceId);
|
||||
operParam.put("action", action);
|
||||
operParam.put("param", param);
|
||||
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(operParam), 0, 2000));
|
||||
operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(Collections.singletonMap("success", success)), 0, 2000));
|
||||
operLog.setStatus(success ? BusinessStatus.SUCCESS.ordinal() : BusinessStatus.FAIL.ordinal());
|
||||
if (!success) {
|
||||
operLog.setErrorMsg(StringUtils.substring(errorMsg, 0, 2000));
|
||||
}
|
||||
operLogService.insertOperlog(operLog);
|
||||
} catch (Exception e) {
|
||||
logger.error("记录sys_oper_log失败, siteId={}, deviceId={}, action={}", siteId, deviceId, action, e);
|
||||
}
|
||||
}
|
||||
|
||||
private ProtectionConstraintVo getProtectionConstraint(String siteId) {
|
||||
ProtectionConstraintVo constraint = redisCache.getCacheObject(RedisKeyConstants.PROTECTION_CONSTRAINT + siteId);
|
||||
if (constraint == null) {
|
||||
return ProtectionConstraintVo.empty();
|
||||
}
|
||||
if (constraint.getPowerLimitRatio() == null) {
|
||||
constraint.setPowerLimitRatio(DEFAULT_PROTECTION_RATIO);
|
||||
}
|
||||
if (constraint.getAllowCharge() == null) {
|
||||
constraint.setAllowCharge(true);
|
||||
}
|
||||
if (constraint.getAllowDischarge() == null) {
|
||||
constraint.setAllowDischarge(true);
|
||||
}
|
||||
if (constraint.getForceStandby() == null) {
|
||||
constraint.setForceStandby(false);
|
||||
}
|
||||
if (constraint.getForceStop() == null) {
|
||||
constraint.setForceStop(false);
|
||||
}
|
||||
if (constraint.getLevel() == null) {
|
||||
constraint.setLevel(0);
|
||||
}
|
||||
return constraint;
|
||||
}
|
||||
|
||||
private StrategyCommandDecision applyProtectionConstraint(BigDecimal targetPower,
|
||||
ChargeStatus targetStatus,
|
||||
EmsStrategyRuntimeConfig runtimeConfig,
|
||||
ProtectionConstraintVo constraint) {
|
||||
if (constraint == null || nullSafeInt(constraint.getLevel()) <= 0) {
|
||||
return new StrategyCommandDecision(targetStatus, safePower(targetPower));
|
||||
}
|
||||
if (Boolean.TRUE.equals(constraint.getForceStop()) || Boolean.TRUE.equals(constraint.getForceStandby())) {
|
||||
return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
|
||||
}
|
||||
if (ChargeStatus.CHARGING.equals(targetStatus) && Boolean.FALSE.equals(constraint.getAllowCharge())) {
|
||||
return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
|
||||
}
|
||||
if (ChargeStatus.DISCHARGING.equals(targetStatus) && Boolean.FALSE.equals(constraint.getAllowDischarge())) {
|
||||
return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
|
||||
}
|
||||
BigDecimal ratio = getPowerLimitRatio(constraint, runtimeConfig);
|
||||
BigDecimal finalPower = safePower(targetPower).multiply(ratio).setScale(POWER_SCALE, RoundingMode.HALF_UP);
|
||||
if (finalPower.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return new StrategyCommandDecision(ChargeStatus.STANDBY, BigDecimal.ZERO);
|
||||
}
|
||||
return new StrategyCommandDecision(targetStatus, finalPower);
|
||||
}
|
||||
|
||||
private BigDecimal getPowerLimitRatio(ProtectionConstraintVo constraint, EmsStrategyRuntimeConfig runtimeConfig) {
|
||||
BigDecimal ratio = constraint.getPowerLimitRatio();
|
||||
if (ratio == null || ratio.compareTo(BigDecimal.ZERO) < 0 || ratio.compareTo(BigDecimal.ONE) > 0) {
|
||||
ratio = DEFAULT_PROTECTION_RATIO;
|
||||
}
|
||||
if (nullSafeInt(constraint.getLevel()) == 1 && DEFAULT_PROTECTION_RATIO.compareTo(ratio) == 0) {
|
||||
BigDecimal deratePercent = runtimeConfig.getProtectL1DeratePercent();
|
||||
if (deratePercent == null || deratePercent.compareTo(BigDecimal.ZERO) < 0 || deratePercent.compareTo(new BigDecimal("100")) > 0) {
|
||||
deratePercent = DEFAULT_PROTECT_L1_DERATE_PERCENT;
|
||||
}
|
||||
ratio = deratePercent.divide(new BigDecimal("100"), POWER_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
return ratio;
|
||||
}
|
||||
|
||||
private BigDecimal safePower(BigDecimal power) {
|
||||
if (power == null || power.compareTo(BigDecimal.ZERO) < 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
private int nullSafeInt(Integer value) {
|
||||
return value == null ? 0 : value;
|
||||
}
|
||||
|
||||
private static class StrategyCommandDecision {
|
||||
private final ChargeStatus chargeStatus;
|
||||
private final BigDecimal power;
|
||||
|
||||
private StrategyCommandDecision(ChargeStatus chargeStatus, BigDecimal power) {
|
||||
this.chargeStatus = chargeStatus;
|
||||
this.power = power;
|
||||
}
|
||||
|
||||
public ChargeStatus getChargeStatus() {
|
||||
return chargeStatus;
|
||||
}
|
||||
|
||||
public BigDecimal getPower() {
|
||||
return power;
|
||||
}
|
||||
}
|
||||
|
||||
// 判断当前时间是否在时间范围内
|
||||
private static boolean isTimeInRange(LocalTime now, Date startTime, Date endTime) {
|
||||
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
|
||||
LocalTime startLocalTime = startTime.toInstant()
|
||||
.atZone(zoneId)
|
||||
.toLocalTime();
|
||||
LocalTime endLocalTime = endTime.toInstant()
|
||||
.atZone(zoneId)
|
||||
.toLocalTime();
|
||||
// 支持跨天时段,如23:00-01:00;边界采用闭区间
|
||||
if (!startLocalTime.isAfter(endLocalTime)) {
|
||||
return !now.isBefore(startLocalTime) && !now.isAfter(endLocalTime);
|
||||
}
|
||||
return !now.isBefore(startLocalTime) || !now.isAfter(endLocalTime);
|
||||
}
|
||||
|
||||
// 判断SOC上限和下限
|
||||
private boolean isSocInRange(EmsStrategyTemp emsStrategyTemp, EmsStrategyRuntimeConfig runtimeConfig) {
|
||||
BigDecimal socDown = runtimeConfig.getSocDown();
|
||||
BigDecimal socUp = runtimeConfig.getSocUp();
|
||||
if (SocLimit.ON.getCode().equals(emsStrategyTemp.getSdcLimit())) {
|
||||
socDown = emsStrategyTemp.getSdcDown();
|
||||
socUp = emsStrategyTemp.getSdcUp();
|
||||
}
|
||||
// 查询电池堆(BMSD) SOC
|
||||
EmsBatteryStack emsBatteryStack = emsBatteryStackMapper.getSiteSumStackInfo(emsStrategyTemp.getSiteId());
|
||||
if (emsBatteryStack == null || emsBatteryStack.getStackSoc() == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 充电阶段判断SOC上限值
|
||||
if (ChargeStatus.CHARGING.getCode().equals(emsStrategyTemp.getChargeStatus()) && emsBatteryStack.getStackSoc().compareTo(socUp) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 放电阶段判断SOC下限值
|
||||
if (ChargeStatus.DISCHARGING.getCode().equals(emsStrategyTemp.getChargeStatus()) && emsBatteryStack.getStackSoc().compareTo(socDown) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private EmsStrategyRuntimeConfig getRuntimeConfig(String siteId) {
|
||||
EmsStrategyRuntimeConfig config = runtimeConfigMapper.selectBySiteId(siteId);
|
||||
if (config == null) {
|
||||
config = new EmsStrategyRuntimeConfig();
|
||||
config.setSiteId(siteId);
|
||||
}
|
||||
if (config.getSocDown() == null) {
|
||||
config.setSocDown(DEFAULT_SOC_DOWN);
|
||||
}
|
||||
if (config.getSocUp() == null) {
|
||||
config.setSocUp(DEFAULT_SOC_UP);
|
||||
}
|
||||
if (config.getAntiReverseThreshold() == null) {
|
||||
config.setAntiReverseThreshold(DEFAULT_ANTI_REVERSE_THRESHOLD);
|
||||
}
|
||||
if (config.getAntiReverseRangePercent() == null) {
|
||||
config.setAntiReverseRangePercent(DEFAULT_ANTI_REVERSE_RANGE_PERCENT);
|
||||
}
|
||||
if (config.getAntiReverseUp() == null) {
|
||||
config.setAntiReverseUp(DEFAULT_ANTI_REVERSE_UP);
|
||||
}
|
||||
if (config.getAntiReversePowerDownPercent() == null) {
|
||||
config.setAntiReversePowerDownPercent(DEFAULT_ANTI_REVERSE_POWER_DOWN_PERCENT);
|
||||
}
|
||||
if (config.getAntiReverseHardStopThreshold() == null) {
|
||||
config.setAntiReverseHardStopThreshold(DEFAULT_ANTI_REVERSE_HARD_STOP_THRESHOLD);
|
||||
}
|
||||
if (config.getPowerSetMultiplier() == null || config.getPowerSetMultiplier().compareTo(BigDecimal.ZERO) <= 0) {
|
||||
config.setPowerSetMultiplier(DEFAULT_POWER_SET_MULTIPLIER);
|
||||
}
|
||||
if (config.getProtectInterveneEnable() == null) {
|
||||
config.setProtectInterveneEnable(DEFAULT_PROTECT_INTERVENE_ENABLE);
|
||||
}
|
||||
if (config.getProtectL1DeratePercent() == null
|
||||
|| config.getProtectL1DeratePercent().compareTo(BigDecimal.ZERO) < 0
|
||||
|| config.getProtectL1DeratePercent().compareTo(new BigDecimal("100")) > 0) {
|
||||
config.setProtectL1DeratePercent(DEFAULT_PROTECT_L1_DERATE_PERCENT);
|
||||
}
|
||||
if (config.getProtectRecoveryStableSeconds() == null || config.getProtectRecoveryStableSeconds() < 0) {
|
||||
config.setProtectRecoveryStableSeconds(DEFAULT_PROTECT_RECOVERY_STABLE_SECONDS);
|
||||
}
|
||||
if (config.getProtectL3LatchEnable() == null) {
|
||||
config.setProtectL3LatchEnable(DEFAULT_PROTECT_L3_LATCH_ENABLE);
|
||||
}
|
||||
if (StringUtils.isEmpty(config.getProtectConflictPolicy())) {
|
||||
config.setProtectConflictPolicy(DEFAULT_PROTECT_CONFLICT_POLICY);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@ -60,4 +60,25 @@ public class CronUtils
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前时间到下一次执行时间间隔的毫秒
|
||||
*/
|
||||
public static long getNextExecutionIntervalMillis(String cronExpression)
|
||||
{
|
||||
try
|
||||
{
|
||||
CronExpression cron = new CronExpression(cronExpression);
|
||||
Date now = new Date();
|
||||
Date nextExecution = cron.getNextValidTimeAfter(now);
|
||||
Date nextExecution2 = cron.getNextValidTimeAfter(nextExecution);
|
||||
|
||||
return nextExecution2.getTime() - nextExecution.getTime();
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -27,6 +27,12 @@
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.influxdb</groupId>
|
||||
<artifactId>influxdb-java</artifactId>
|
||||
<version>2.24</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,61 @@
|
||||
package com.xzzn.ems.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "weather.api")
|
||||
public class WeatherApiProperties {
|
||||
|
||||
/**
|
||||
* 是否启用天气同步
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* 天气接口基础地址
|
||||
*/
|
||||
private String baseUrl = "https://archive-api.open-meteo.com/v1/archive";
|
||||
|
||||
/**
|
||||
* 可选接口鉴权字段
|
||||
*/
|
||||
private String apiKey;
|
||||
|
||||
/**
|
||||
* 时区参数
|
||||
*/
|
||||
private String timezone = "Asia/Shanghai";
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getTimezone() {
|
||||
return timezone;
|
||||
}
|
||||
|
||||
public void setTimezone(String timezone) {
|
||||
this.timezone = timezone;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 告警点位匹配数据对象 ems_alarm_match_data
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-09-22
|
||||
*/
|
||||
public class EmsAlarmMatchData extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** $column.columnComment */
|
||||
private Long id;
|
||||
|
||||
/** 告警点位 */
|
||||
@Excel(name = "告警点位")
|
||||
private String point;
|
||||
|
||||
/** 告警值 */
|
||||
@Excel(name = "告警值")
|
||||
private Long alarmData;
|
||||
|
||||
/** 告警描述 */
|
||||
@Excel(name = "告警描述")
|
||||
private String alarmDescription;
|
||||
|
||||
/** 站点id */
|
||||
@Excel(name = "站点id")
|
||||
private String siteId;
|
||||
|
||||
/** 设备类型 */
|
||||
@Excel(name = "设备类型")
|
||||
private String deviceCategory;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setPoint(String point)
|
||||
{
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
public String getPoint()
|
||||
{
|
||||
return point;
|
||||
}
|
||||
|
||||
public void setAlarmData(Long alarmData)
|
||||
{
|
||||
this.alarmData = alarmData;
|
||||
}
|
||||
|
||||
public Long getAlarmData()
|
||||
{
|
||||
return alarmData;
|
||||
}
|
||||
|
||||
public void setAlarmDescription(String alarmDescription)
|
||||
{
|
||||
this.alarmDescription = alarmDescription;
|
||||
}
|
||||
|
||||
public String getAlarmDescription()
|
||||
{
|
||||
return alarmDescription;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId)
|
||||
{
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getSiteId()
|
||||
{
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setDeviceCategory(String deviceCategory)
|
||||
{
|
||||
this.deviceCategory = deviceCategory;
|
||||
}
|
||||
|
||||
public String getDeviceCategory()
|
||||
{
|
||||
return deviceCategory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("point", getPoint())
|
||||
.append("alarmData", getAlarmData())
|
||||
.append("alarmDescription", getAlarmDescription())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceCategory", getDeviceCategory())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -24,8 +24,8 @@ public class EmsAlarmRecords extends BaseEntity
|
||||
@Excel(name = "设备类型")
|
||||
private String deviceType;
|
||||
|
||||
/** 告警等级 */
|
||||
@Excel(name = "告警等级")
|
||||
/** 告警等级:A-提示 B-一般 C-严重 D紧急 */
|
||||
@Excel(name = "告警等级:A-提示 B-一般 C-严重 D紧急")
|
||||
private String alarmLevel;
|
||||
|
||||
/** 告警内容 */
|
||||
@ -42,8 +42,12 @@ public class EmsAlarmRecords extends BaseEntity
|
||||
@Excel(name = "告警结束时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date alarmEndTime;
|
||||
|
||||
/** 状态 */
|
||||
@Excel(name = "状态")
|
||||
/** 告警点位 */
|
||||
@Excel(name = "告警点位")
|
||||
private String alarmPoint;
|
||||
|
||||
/** 状态:0-待处理 1-已处理 2-处理中 */
|
||||
@Excel(name = "状态:0-待处理 1-已处理 2-处理中")
|
||||
private String status;
|
||||
|
||||
/** 站点id */
|
||||
@ -54,10 +58,6 @@ public class EmsAlarmRecords extends BaseEntity
|
||||
@Excel(name = "设备唯一标识符")
|
||||
private String deviceId;
|
||||
|
||||
/** 设备名称,用于标识设备 */
|
||||
@Excel(name = "设备名称,用于标识设备")
|
||||
private String deviceName;
|
||||
|
||||
/** 工单号(规则:T+日期+6位随机) */
|
||||
@Excel(name = "工单号", readConverterExp = "规=则:T+日期+6位随机")
|
||||
private String ticketNo;
|
||||
@ -122,6 +122,16 @@ public class EmsAlarmRecords extends BaseEntity
|
||||
return alarmEndTime;
|
||||
}
|
||||
|
||||
public void setAlarmPoint(String alarmPoint)
|
||||
{
|
||||
this.alarmPoint = alarmPoint;
|
||||
}
|
||||
|
||||
public String getAlarmPoint()
|
||||
{
|
||||
return alarmPoint;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
@ -152,24 +162,16 @@ public class EmsAlarmRecords extends BaseEntity
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName)
|
||||
public void setTicketNo(String ticketNo)
|
||||
{
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
|
||||
public String getDeviceName()
|
||||
{
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public String getTicketNo() {
|
||||
return ticketNo;
|
||||
}
|
||||
|
||||
public void setTicketNo(String ticketNo) {
|
||||
this.ticketNo = ticketNo;
|
||||
}
|
||||
|
||||
public String getTicketNo()
|
||||
{
|
||||
return ticketNo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
@ -179,6 +181,7 @@ public class EmsAlarmRecords extends BaseEntity
|
||||
.append("alarmContent", getAlarmContent())
|
||||
.append("alarmStartTime", getAlarmStartTime())
|
||||
.append("alarmEndTime", getAlarmEndTime())
|
||||
.append("alarmPoint", getAlarmPoint())
|
||||
.append("status", getStatus())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
@ -187,7 +190,6 @@ public class EmsAlarmRecords extends BaseEntity
|
||||
.append("remark", getRemark())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("deviceName", getDeviceName())
|
||||
.append("ticketNo", getTicketNo())
|
||||
.toString();
|
||||
}
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 电池簇数据对象 ems_battery_cluster
|
||||
@ -20,8 +24,13 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
/** */
|
||||
private Long id;
|
||||
|
||||
/** 工作状态:0-正常 1-异常 2-停止 */
|
||||
@Excel(name = "工作状态:0-正常 1-异常 2-停止")
|
||||
/** 数据更新时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date dataUpdateTime;
|
||||
|
||||
/** 工作状态:0-运行 1-停机 2-故障 */
|
||||
@Excel(name = "工作状态:0-运行 1-停机 2-故障")
|
||||
private String workStatus;
|
||||
|
||||
/** 与PCS通信状态:0-正常 1-通信中断 2-异常 */
|
||||
@ -146,7 +155,7 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
|
||||
/** 最高单体电压对应点号 */
|
||||
@Excel(name = "最高单体电压对应点号")
|
||||
private Long maxCellVoltageId;
|
||||
private String maxCellVoltageId;
|
||||
|
||||
/** 最低单体电压 */
|
||||
@Excel(name = "最低单体电压")
|
||||
@ -154,7 +163,7 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
|
||||
/** 最低单体电压对应点号 */
|
||||
@Excel(name = "最低单体电压对应点号")
|
||||
private Long minCellVoltageId;
|
||||
private String minCellVoltageId;
|
||||
|
||||
/** 最高单体温度 */
|
||||
@Excel(name = "最高单体温度")
|
||||
@ -162,7 +171,7 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
|
||||
/** 最高单体温度对应点号 */
|
||||
@Excel(name = "最高单体温度对应点号")
|
||||
private Long maxCellTempId;
|
||||
private String maxCellTempId;
|
||||
|
||||
/** 最低单体温度 */
|
||||
@Excel(name = "最低单体温度")
|
||||
@ -170,7 +179,7 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
|
||||
/** 最低单体温度对应点号 */
|
||||
@Excel(name = "最低单体温度对应点号")
|
||||
private Long minCellTempId;
|
||||
private String minCellTempId;
|
||||
|
||||
/** 最高单体SOC */
|
||||
@Excel(name = "最高单体SOC")
|
||||
@ -178,7 +187,7 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
|
||||
/** 最高单体SOC对应点号 */
|
||||
@Excel(name = "最高单体SOC对应点号")
|
||||
private Long maxCellSocId;
|
||||
private String maxCellSocId;
|
||||
|
||||
/** 最低单体SOC */
|
||||
@Excel(name = "最低单体SOC")
|
||||
@ -186,7 +195,7 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
|
||||
/** 最低单体SOC对应点号 */
|
||||
@Excel(name = "最低单体SOC对应点号")
|
||||
private Long minCellSocId;
|
||||
private String minCellSocId;
|
||||
|
||||
/** 最高单体SOH */
|
||||
@Excel(name = "最高单体SOH")
|
||||
@ -194,7 +203,7 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
|
||||
/** 最高单体SOH对应点号 */
|
||||
@Excel(name = "最高单体SOH对应点号")
|
||||
private Long maxCellSohId;
|
||||
private String maxCellSohId;
|
||||
|
||||
/** 最低单体SOH */
|
||||
@Excel(name = "最低单体SOH")
|
||||
@ -202,7 +211,7 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
|
||||
/** 最低单体SOH对应点号 */
|
||||
@Excel(name = "最低单体SOH对应点号")
|
||||
private Long minCellSohId;
|
||||
private String minCellSohId;
|
||||
|
||||
/** 单次累计充电电量 */
|
||||
@Excel(name = "单次累计充电电量")
|
||||
@ -222,6 +231,14 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
return id;
|
||||
}
|
||||
|
||||
public Date getDataUpdateTime() {
|
||||
return dataUpdateTime;
|
||||
}
|
||||
|
||||
public void setDataUpdateTime(Date dataUpdateTime) {
|
||||
this.dataUpdateTime = dataUpdateTime;
|
||||
}
|
||||
|
||||
public void setWorkStatus(String workStatus)
|
||||
{
|
||||
this.workStatus = workStatus;
|
||||
@ -532,12 +549,12 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
return maxCellVoltage;
|
||||
}
|
||||
|
||||
public void setMaxCellVoltageId(Long maxCellVoltageId)
|
||||
public void setMaxCellVoltageId(String maxCellVoltageId)
|
||||
{
|
||||
this.maxCellVoltageId = maxCellVoltageId;
|
||||
this.maxCellVoltageId = StringUtils.fillThreeDigits(maxCellVoltageId);
|
||||
}
|
||||
|
||||
public Long getMaxCellVoltageId()
|
||||
public String getMaxCellVoltageId()
|
||||
{
|
||||
return maxCellVoltageId;
|
||||
}
|
||||
@ -552,12 +569,12 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
return minCellVoltage;
|
||||
}
|
||||
|
||||
public void setMinCellVoltageId(Long minCellVoltageId)
|
||||
public void setMinCellVoltageId(String minCellVoltageId)
|
||||
{
|
||||
this.minCellVoltageId = minCellVoltageId;
|
||||
this.minCellVoltageId = StringUtils.fillThreeDigits(minCellVoltageId);
|
||||
}
|
||||
|
||||
public Long getMinCellVoltageId()
|
||||
public String getMinCellVoltageId()
|
||||
{
|
||||
return minCellVoltageId;
|
||||
}
|
||||
@ -572,12 +589,12 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
return maxCellTemp;
|
||||
}
|
||||
|
||||
public void setMaxCellTempId(Long maxCellTempId)
|
||||
public void setMaxCellTempId(String maxCellTempId)
|
||||
{
|
||||
this.maxCellTempId = maxCellTempId;
|
||||
this.maxCellTempId = StringUtils.fillThreeDigits(maxCellTempId);
|
||||
}
|
||||
|
||||
public Long getMaxCellTempId()
|
||||
public String getMaxCellTempId()
|
||||
{
|
||||
return maxCellTempId;
|
||||
}
|
||||
@ -592,12 +609,12 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
return minCellTemp;
|
||||
}
|
||||
|
||||
public void setMinCellTempId(Long minCellTempId)
|
||||
public void setMinCellTempId(String minCellTempId)
|
||||
{
|
||||
this.minCellTempId = minCellTempId;
|
||||
this.minCellTempId = StringUtils.fillThreeDigits(minCellTempId);
|
||||
}
|
||||
|
||||
public Long getMinCellTempId()
|
||||
public String getMinCellTempId()
|
||||
{
|
||||
return minCellTempId;
|
||||
}
|
||||
@ -612,12 +629,12 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
return maxCellSoc;
|
||||
}
|
||||
|
||||
public void setMaxCellSocId(Long maxCellSocId)
|
||||
public void setMaxCellSocId(String maxCellSocId)
|
||||
{
|
||||
this.maxCellSocId = maxCellSocId;
|
||||
this.maxCellSocId = StringUtils.fillThreeDigits(maxCellSocId);
|
||||
}
|
||||
|
||||
public Long getMaxCellSocId()
|
||||
public String getMaxCellSocId()
|
||||
{
|
||||
return maxCellSocId;
|
||||
}
|
||||
@ -632,12 +649,12 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
return minCellSoc;
|
||||
}
|
||||
|
||||
public void setMinCellSocId(Long minCellSocId)
|
||||
public void setMinCellSocId(String minCellSocId)
|
||||
{
|
||||
this.minCellSocId = minCellSocId;
|
||||
this.minCellSocId = StringUtils.fillThreeDigits(minCellSocId);
|
||||
}
|
||||
|
||||
public Long getMinCellSocId()
|
||||
public String getMinCellSocId()
|
||||
{
|
||||
return minCellSocId;
|
||||
}
|
||||
@ -652,12 +669,12 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
return maxCellSoh;
|
||||
}
|
||||
|
||||
public void setMaxCellSohId(Long maxCellSohId)
|
||||
public void setMaxCellSohId(String maxCellSohId)
|
||||
{
|
||||
this.maxCellSohId = maxCellSohId;
|
||||
this.maxCellSohId = StringUtils.fillThreeDigits(maxCellSohId);
|
||||
}
|
||||
|
||||
public Long getMaxCellSohId()
|
||||
public String getMaxCellSohId()
|
||||
{
|
||||
return maxCellSohId;
|
||||
}
|
||||
@ -672,12 +689,12 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
return minCellSoh;
|
||||
}
|
||||
|
||||
public void setMinCellSohId(Long minCellSohId)
|
||||
public void setMinCellSohId(String minCellSohId)
|
||||
{
|
||||
this.minCellSohId = minCellSohId;
|
||||
this.minCellSohId = StringUtils.fillThreeDigits(minCellSohId);
|
||||
}
|
||||
|
||||
public Long getMinCellSohId()
|
||||
public String getMinCellSohId()
|
||||
{
|
||||
return minCellSohId;
|
||||
}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 单体电池实时数据对象 ems_battery_data
|
||||
@ -70,6 +72,10 @@ public class EmsBatteryData extends BaseEntity
|
||||
@Excel(name = "单体电池内阻")
|
||||
private BigDecimal interResistance;
|
||||
|
||||
/** 单体电池电流 */
|
||||
@Excel(name = "单体电池电流")
|
||||
private BigDecimal current;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
@ -200,6 +206,14 @@ public class EmsBatteryData extends BaseEntity
|
||||
return interResistance;
|
||||
}
|
||||
|
||||
public BigDecimal getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public void setCurrent(BigDecimal current) {
|
||||
this.current = current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
@ -221,6 +235,7 @@ public class EmsBatteryData extends BaseEntity
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("clusterDeviceId", getClusterDeviceId())
|
||||
.append("interResistance", getInterResistance())
|
||||
.append("current", getCurrent())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,257 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
/**
|
||||
* 单体电池天级数据对象 ems_battery_data_day
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-08-20
|
||||
*/
|
||||
public class EmsBatteryDataDay extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** $column.columnComment */
|
||||
private Long id;
|
||||
|
||||
/** 电池堆 */
|
||||
@Excel(name = "电池堆")
|
||||
private String batteryPack;
|
||||
|
||||
/** 电池簇 */
|
||||
@Excel(name = "电池簇")
|
||||
private String batteryCluster;
|
||||
|
||||
/** 单体编号 */
|
||||
@Excel(name = "单体编号")
|
||||
private String batteryCellId;
|
||||
|
||||
/** 电压 (V) */
|
||||
@Excel(name = "电压 (V)")
|
||||
private BigDecimal voltage;
|
||||
|
||||
/** 温度 (℃) */
|
||||
@Excel(name = "温度 (℃)")
|
||||
private BigDecimal temperature;
|
||||
|
||||
/** SOC (%) */
|
||||
@Excel(name = "SOC (%)")
|
||||
private BigDecimal soc;
|
||||
|
||||
/** SOH (%) */
|
||||
@Excel(name = "SOH (%)")
|
||||
private BigDecimal soh;
|
||||
|
||||
/** 数据采集时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date dataTimestamp;
|
||||
|
||||
/** 站点id */
|
||||
@Excel(name = "站点id")
|
||||
private String siteId;
|
||||
|
||||
/** 设备唯一标识符 */
|
||||
@Excel(name = "设备唯一标识符")
|
||||
private String deviceId;
|
||||
|
||||
/** 簇设备id */
|
||||
@Excel(name = "簇设备id")
|
||||
private String clusterDeviceId;
|
||||
|
||||
/** 单体电池内阻 */
|
||||
@Excel(name = "单体电池内阻")
|
||||
private BigDecimal interResistance;
|
||||
|
||||
/** 天级时间维度 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "天级时间维度", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date dayTime;
|
||||
|
||||
/** 单体电池电流 */
|
||||
@Excel(name = "单体电池电流")
|
||||
private BigDecimal current;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setBatteryPack(String batteryPack)
|
||||
{
|
||||
this.batteryPack = batteryPack;
|
||||
}
|
||||
|
||||
public String getBatteryPack()
|
||||
{
|
||||
return batteryPack;
|
||||
}
|
||||
|
||||
public void setBatteryCluster(String batteryCluster)
|
||||
{
|
||||
this.batteryCluster = batteryCluster;
|
||||
}
|
||||
|
||||
public String getBatteryCluster()
|
||||
{
|
||||
return batteryCluster;
|
||||
}
|
||||
|
||||
public void setBatteryCellId(String batteryCellId)
|
||||
{
|
||||
this.batteryCellId = batteryCellId;
|
||||
}
|
||||
|
||||
public String getBatteryCellId()
|
||||
{
|
||||
return batteryCellId;
|
||||
}
|
||||
|
||||
public void setVoltage(BigDecimal voltage)
|
||||
{
|
||||
this.voltage = voltage;
|
||||
}
|
||||
|
||||
public BigDecimal getVoltage()
|
||||
{
|
||||
return voltage;
|
||||
}
|
||||
|
||||
public void setTemperature(BigDecimal temperature)
|
||||
{
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public BigDecimal getTemperature()
|
||||
{
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setSoc(BigDecimal soc)
|
||||
{
|
||||
this.soc = soc;
|
||||
}
|
||||
|
||||
public BigDecimal getSoc()
|
||||
{
|
||||
return soc;
|
||||
}
|
||||
|
||||
public void setSoh(BigDecimal soh)
|
||||
{
|
||||
this.soh = soh;
|
||||
}
|
||||
|
||||
public BigDecimal getSoh()
|
||||
{
|
||||
return soh;
|
||||
}
|
||||
|
||||
public void setDataTimestamp(Date dataTimestamp)
|
||||
{
|
||||
this.dataTimestamp = dataTimestamp;
|
||||
}
|
||||
|
||||
public Date getDataTimestamp()
|
||||
{
|
||||
return dataTimestamp;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId)
|
||||
{
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getSiteId()
|
||||
{
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId)
|
||||
{
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceId()
|
||||
{
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setClusterDeviceId(String clusterDeviceId)
|
||||
{
|
||||
this.clusterDeviceId = clusterDeviceId;
|
||||
}
|
||||
|
||||
public String getClusterDeviceId()
|
||||
{
|
||||
return clusterDeviceId;
|
||||
}
|
||||
|
||||
public void setInterResistance(BigDecimal interResistance)
|
||||
{
|
||||
this.interResistance = interResistance;
|
||||
}
|
||||
|
||||
public BigDecimal getInterResistance()
|
||||
{
|
||||
return interResistance;
|
||||
}
|
||||
|
||||
public void setDayTime(Date dayTime)
|
||||
{
|
||||
this.dayTime = dayTime;
|
||||
}
|
||||
|
||||
public Date getDayTime()
|
||||
{
|
||||
return dayTime;
|
||||
}
|
||||
|
||||
public BigDecimal getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public void setCurrent(BigDecimal current) {
|
||||
this.current = current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("batteryPack", getBatteryPack())
|
||||
.append("batteryCluster", getBatteryCluster())
|
||||
.append("batteryCellId", getBatteryCellId())
|
||||
.append("voltage", getVoltage())
|
||||
.append("temperature", getTemperature())
|
||||
.append("soc", getSoc())
|
||||
.append("soh", getSoh())
|
||||
.append("dataTimestamp", getDataTimestamp())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("clusterDeviceId", getClusterDeviceId())
|
||||
.append("interResistance", getInterResistance())
|
||||
.append("dayTime", getDayTime())
|
||||
.append("current", getCurrent())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,242 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 单体电池小时级数据对象 ems_battery_data_hour
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-08-19
|
||||
*/
|
||||
public class EmsBatteryDataHour extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** $column.columnComment */
|
||||
private Long id;
|
||||
|
||||
/** 电池堆 */
|
||||
@Excel(name = "电池堆")
|
||||
private String batteryPack;
|
||||
|
||||
/** 电池簇 */
|
||||
@Excel(name = "电池簇")
|
||||
private String batteryCluster;
|
||||
|
||||
/** 单体编号 */
|
||||
@Excel(name = "单体编号")
|
||||
private String batteryCellId;
|
||||
|
||||
/** 电压 (V) */
|
||||
@Excel(name = "电压 (V)")
|
||||
private BigDecimal voltage;
|
||||
|
||||
/** 温度 (℃) */
|
||||
@Excel(name = "温度 (℃)")
|
||||
private BigDecimal temperature;
|
||||
|
||||
/** SOC (%) */
|
||||
@Excel(name = "SOC (%)")
|
||||
private BigDecimal soc;
|
||||
|
||||
/** SOH (%) */
|
||||
@Excel(name = "SOH (%)")
|
||||
private BigDecimal soh;
|
||||
|
||||
/** 数据采集时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date dataTimestamp;
|
||||
|
||||
/** 站点id */
|
||||
@Excel(name = "站点id")
|
||||
private String siteId;
|
||||
|
||||
/** 设备唯一标识符 */
|
||||
@Excel(name = "设备唯一标识符")
|
||||
private String deviceId;
|
||||
|
||||
/** 簇设备id */
|
||||
@Excel(name = "簇设备id")
|
||||
private String clusterDeviceId;
|
||||
|
||||
/** 单体电池内阻 */
|
||||
@Excel(name = "单体电池内阻")
|
||||
private BigDecimal interResistance;
|
||||
|
||||
/** 小时级时间维度 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "小时级时间维度", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date hourTime;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setBatteryPack(String batteryPack)
|
||||
{
|
||||
this.batteryPack = batteryPack;
|
||||
}
|
||||
|
||||
public String getBatteryPack()
|
||||
{
|
||||
return batteryPack;
|
||||
}
|
||||
|
||||
public void setBatteryCluster(String batteryCluster)
|
||||
{
|
||||
this.batteryCluster = batteryCluster;
|
||||
}
|
||||
|
||||
public String getBatteryCluster()
|
||||
{
|
||||
return batteryCluster;
|
||||
}
|
||||
|
||||
public void setBatteryCellId(String batteryCellId)
|
||||
{
|
||||
this.batteryCellId = batteryCellId;
|
||||
}
|
||||
|
||||
public String getBatteryCellId()
|
||||
{
|
||||
return batteryCellId;
|
||||
}
|
||||
|
||||
public void setVoltage(BigDecimal voltage)
|
||||
{
|
||||
this.voltage = voltage;
|
||||
}
|
||||
|
||||
public BigDecimal getVoltage()
|
||||
{
|
||||
return voltage;
|
||||
}
|
||||
|
||||
public void setTemperature(BigDecimal temperature)
|
||||
{
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public BigDecimal getTemperature()
|
||||
{
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setSoc(BigDecimal soc)
|
||||
{
|
||||
this.soc = soc;
|
||||
}
|
||||
|
||||
public BigDecimal getSoc()
|
||||
{
|
||||
return soc;
|
||||
}
|
||||
|
||||
public void setSoh(BigDecimal soh)
|
||||
{
|
||||
this.soh = soh;
|
||||
}
|
||||
|
||||
public BigDecimal getSoh()
|
||||
{
|
||||
return soh;
|
||||
}
|
||||
|
||||
public void setDataTimestamp(Date dataTimestamp)
|
||||
{
|
||||
this.dataTimestamp = dataTimestamp;
|
||||
}
|
||||
|
||||
public Date getDataTimestamp()
|
||||
{
|
||||
return dataTimestamp;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId)
|
||||
{
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getSiteId()
|
||||
{
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId)
|
||||
{
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceId()
|
||||
{
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setClusterDeviceId(String clusterDeviceId)
|
||||
{
|
||||
this.clusterDeviceId = clusterDeviceId;
|
||||
}
|
||||
|
||||
public String getClusterDeviceId()
|
||||
{
|
||||
return clusterDeviceId;
|
||||
}
|
||||
|
||||
public void setInterResistance(BigDecimal interResistance)
|
||||
{
|
||||
this.interResistance = interResistance;
|
||||
}
|
||||
|
||||
public BigDecimal getInterResistance()
|
||||
{
|
||||
return interResistance;
|
||||
}
|
||||
|
||||
public void setHourTime(Date hourTime)
|
||||
{
|
||||
this.hourTime = hourTime;
|
||||
}
|
||||
|
||||
public Date getHourTime()
|
||||
{
|
||||
return hourTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("batteryPack", getBatteryPack())
|
||||
.append("batteryCluster", getBatteryCluster())
|
||||
.append("batteryCellId", getBatteryCellId())
|
||||
.append("voltage", getVoltage())
|
||||
.append("temperature", getTemperature())
|
||||
.append("soc", getSoc())
|
||||
.append("soh", getSoh())
|
||||
.append("dataTimestamp", getDataTimestamp())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("clusterDeviceId", getClusterDeviceId())
|
||||
.append("interResistance", getInterResistance())
|
||||
.append("hourTime", getHourTime())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,226 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 单体电池分钟级数据对象 ems_battery_data_minutes
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-08-18
|
||||
*/
|
||||
public class EmsBatteryDataMinutes extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** $column.columnComment */
|
||||
private Long id;
|
||||
|
||||
/** 电池堆 */
|
||||
@Excel(name = "电池堆")
|
||||
private String batteryPack;
|
||||
|
||||
/** 电池簇 */
|
||||
@Excel(name = "电池簇")
|
||||
private String batteryCluster;
|
||||
|
||||
/** 单体编号 */
|
||||
@Excel(name = "单体编号")
|
||||
private String batteryCellId;
|
||||
|
||||
/** 电压 (V) */
|
||||
@Excel(name = "电压 (V)")
|
||||
private BigDecimal voltage;
|
||||
|
||||
/** 温度 (℃) */
|
||||
@Excel(name = "温度 (℃)")
|
||||
private BigDecimal temperature;
|
||||
|
||||
/** SOC (%) */
|
||||
@Excel(name = "SOC (%)")
|
||||
private BigDecimal soc;
|
||||
|
||||
/** SOH (%) */
|
||||
@Excel(name = "SOH (%)")
|
||||
private BigDecimal soh;
|
||||
|
||||
/** 数据采集时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date dataTimestamp;
|
||||
|
||||
/** 站点id */
|
||||
@Excel(name = "站点id")
|
||||
private String siteId;
|
||||
|
||||
/** 设备唯一标识符 */
|
||||
@Excel(name = "设备唯一标识符")
|
||||
private String deviceId;
|
||||
|
||||
/** 簇设备id */
|
||||
@Excel(name = "簇设备id")
|
||||
private String clusterDeviceId;
|
||||
|
||||
/** 单体电池内阻 */
|
||||
@Excel(name = "单体电池内阻")
|
||||
private BigDecimal interResistance;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setBatteryPack(String batteryPack)
|
||||
{
|
||||
this.batteryPack = batteryPack;
|
||||
}
|
||||
|
||||
public String getBatteryPack()
|
||||
{
|
||||
return batteryPack;
|
||||
}
|
||||
|
||||
public void setBatteryCluster(String batteryCluster)
|
||||
{
|
||||
this.batteryCluster = batteryCluster;
|
||||
}
|
||||
|
||||
public String getBatteryCluster()
|
||||
{
|
||||
return batteryCluster;
|
||||
}
|
||||
|
||||
public void setBatteryCellId(String batteryCellId)
|
||||
{
|
||||
this.batteryCellId = batteryCellId;
|
||||
}
|
||||
|
||||
public String getBatteryCellId()
|
||||
{
|
||||
return batteryCellId;
|
||||
}
|
||||
|
||||
public void setVoltage(BigDecimal voltage)
|
||||
{
|
||||
this.voltage = voltage;
|
||||
}
|
||||
|
||||
public BigDecimal getVoltage()
|
||||
{
|
||||
return voltage;
|
||||
}
|
||||
|
||||
public void setTemperature(BigDecimal temperature)
|
||||
{
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public BigDecimal getTemperature()
|
||||
{
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setSoc(BigDecimal soc)
|
||||
{
|
||||
this.soc = soc;
|
||||
}
|
||||
|
||||
public BigDecimal getSoc()
|
||||
{
|
||||
return soc;
|
||||
}
|
||||
|
||||
public void setSoh(BigDecimal soh)
|
||||
{
|
||||
this.soh = soh;
|
||||
}
|
||||
|
||||
public BigDecimal getSoh()
|
||||
{
|
||||
return soh;
|
||||
}
|
||||
|
||||
public void setDataTimestamp(Date dataTimestamp)
|
||||
{
|
||||
this.dataTimestamp = dataTimestamp;
|
||||
}
|
||||
|
||||
public Date getDataTimestamp()
|
||||
{
|
||||
return dataTimestamp;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId)
|
||||
{
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getSiteId()
|
||||
{
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId)
|
||||
{
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceId()
|
||||
{
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setClusterDeviceId(String clusterDeviceId)
|
||||
{
|
||||
this.clusterDeviceId = clusterDeviceId;
|
||||
}
|
||||
|
||||
public String getClusterDeviceId()
|
||||
{
|
||||
return clusterDeviceId;
|
||||
}
|
||||
|
||||
public void setInterResistance(BigDecimal interResistance)
|
||||
{
|
||||
this.interResistance = interResistance;
|
||||
}
|
||||
|
||||
public BigDecimal getInterResistance()
|
||||
{
|
||||
return interResistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("batteryPack", getBatteryPack())
|
||||
.append("batteryCluster", getBatteryCluster())
|
||||
.append("batteryCellId", getBatteryCellId())
|
||||
.append("voltage", getVoltage())
|
||||
.append("temperature", getTemperature())
|
||||
.append("soc", getSoc())
|
||||
.append("soh", getSoh())
|
||||
.append("dataTimestamp", getDataTimestamp())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("clusterDeviceId", getClusterDeviceId())
|
||||
.append("interResistance", getInterResistance())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,242 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 单体电池月级数据对象 ems_battery_data_month
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-08-22
|
||||
*/
|
||||
public class EmsBatteryDataMonth extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** $column.columnComment */
|
||||
private Long id;
|
||||
|
||||
/** 电池堆 */
|
||||
@Excel(name = "电池堆")
|
||||
private String batteryPack;
|
||||
|
||||
/** 电池簇 */
|
||||
@Excel(name = "电池簇")
|
||||
private String batteryCluster;
|
||||
|
||||
/** 单体编号 */
|
||||
@Excel(name = "单体编号")
|
||||
private String batteryCellId;
|
||||
|
||||
/** 电压 (V) */
|
||||
@Excel(name = "电压 (V)")
|
||||
private BigDecimal voltage;
|
||||
|
||||
/** 温度 (℃) */
|
||||
@Excel(name = "温度 (℃)")
|
||||
private BigDecimal temperature;
|
||||
|
||||
/** SOC (%) */
|
||||
@Excel(name = "SOC (%)")
|
||||
private BigDecimal soc;
|
||||
|
||||
/** SOH (%) */
|
||||
@Excel(name = "SOH (%)")
|
||||
private BigDecimal soh;
|
||||
|
||||
/** 数据采集时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "数据采集时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date dataTimestamp;
|
||||
|
||||
/** 站点id */
|
||||
@Excel(name = "站点id")
|
||||
private String siteId;
|
||||
|
||||
/** 设备唯一标识符 */
|
||||
@Excel(name = "设备唯一标识符")
|
||||
private String deviceId;
|
||||
|
||||
/** 簇设备id */
|
||||
@Excel(name = "簇设备id")
|
||||
private String clusterDeviceId;
|
||||
|
||||
/** 单体电池内阻 */
|
||||
@Excel(name = "单体电池内阻")
|
||||
private BigDecimal interResistance;
|
||||
|
||||
/** 月级时间维度 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "月级时间维度", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date monthTime;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setBatteryPack(String batteryPack)
|
||||
{
|
||||
this.batteryPack = batteryPack;
|
||||
}
|
||||
|
||||
public String getBatteryPack()
|
||||
{
|
||||
return batteryPack;
|
||||
}
|
||||
|
||||
public void setBatteryCluster(String batteryCluster)
|
||||
{
|
||||
this.batteryCluster = batteryCluster;
|
||||
}
|
||||
|
||||
public String getBatteryCluster()
|
||||
{
|
||||
return batteryCluster;
|
||||
}
|
||||
|
||||
public void setBatteryCellId(String batteryCellId)
|
||||
{
|
||||
this.batteryCellId = batteryCellId;
|
||||
}
|
||||
|
||||
public String getBatteryCellId()
|
||||
{
|
||||
return batteryCellId;
|
||||
}
|
||||
|
||||
public void setVoltage(BigDecimal voltage)
|
||||
{
|
||||
this.voltage = voltage;
|
||||
}
|
||||
|
||||
public BigDecimal getVoltage()
|
||||
{
|
||||
return voltage;
|
||||
}
|
||||
|
||||
public void setTemperature(BigDecimal temperature)
|
||||
{
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public BigDecimal getTemperature()
|
||||
{
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setSoc(BigDecimal soc)
|
||||
{
|
||||
this.soc = soc;
|
||||
}
|
||||
|
||||
public BigDecimal getSoc()
|
||||
{
|
||||
return soc;
|
||||
}
|
||||
|
||||
public void setSoh(BigDecimal soh)
|
||||
{
|
||||
this.soh = soh;
|
||||
}
|
||||
|
||||
public BigDecimal getSoh()
|
||||
{
|
||||
return soh;
|
||||
}
|
||||
|
||||
public void setDataTimestamp(Date dataTimestamp)
|
||||
{
|
||||
this.dataTimestamp = dataTimestamp;
|
||||
}
|
||||
|
||||
public Date getDataTimestamp()
|
||||
{
|
||||
return dataTimestamp;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId)
|
||||
{
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getSiteId()
|
||||
{
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId)
|
||||
{
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceId()
|
||||
{
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setClusterDeviceId(String clusterDeviceId)
|
||||
{
|
||||
this.clusterDeviceId = clusterDeviceId;
|
||||
}
|
||||
|
||||
public String getClusterDeviceId()
|
||||
{
|
||||
return clusterDeviceId;
|
||||
}
|
||||
|
||||
public void setInterResistance(BigDecimal interResistance)
|
||||
{
|
||||
this.interResistance = interResistance;
|
||||
}
|
||||
|
||||
public BigDecimal getInterResistance()
|
||||
{
|
||||
return interResistance;
|
||||
}
|
||||
|
||||
public void setMonthTime(Date monthTime)
|
||||
{
|
||||
this.monthTime = monthTime;
|
||||
}
|
||||
|
||||
public Date getMonthTime()
|
||||
{
|
||||
return monthTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("batteryPack", getBatteryPack())
|
||||
.append("batteryCluster", getBatteryCluster())
|
||||
.append("batteryCellId", getBatteryCellId())
|
||||
.append("voltage", getVoltage())
|
||||
.append("temperature", getTemperature())
|
||||
.append("soc", getSoc())
|
||||
.append("soh", getSoh())
|
||||
.append("dataTimestamp", getDataTimestamp())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("clusterDeviceId", getClusterDeviceId())
|
||||
.append("interResistance", getInterResistance())
|
||||
.append("monthTime", getMonthTime())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,14 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 电池堆数据对象 ems_battery_stack
|
||||
@ -20,8 +23,13 @@ public class EmsBatteryStack extends BaseEntity
|
||||
/** */
|
||||
private Long id;
|
||||
|
||||
/** 工作状态:0-正常 1-异常 2-停止 */
|
||||
@Excel(name = "工作状态:0-正常 1-异常 2-停止")
|
||||
/** 数据更新时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date dataUpdateTime;
|
||||
|
||||
/** 工作状态:0-运行 1-停机 2-故障 */
|
||||
@Excel(name = "工作状态:0-运行 1-停机 2-故障")
|
||||
private String workStatus;
|
||||
|
||||
/** 与PCS通信状态:0-正常 1-通信中断 2-异常 */
|
||||
@ -254,6 +262,14 @@ public class EmsBatteryStack extends BaseEntity
|
||||
return id;
|
||||
}
|
||||
|
||||
public Date getDataUpdateTime() {
|
||||
return dataUpdateTime;
|
||||
}
|
||||
|
||||
public void setDataUpdateTime(Date dataUpdateTime) {
|
||||
this.dataUpdateTime = dataUpdateTime;
|
||||
}
|
||||
|
||||
public void setWorkStatus(String workStatus)
|
||||
{
|
||||
this.workStatus = workStatus;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user