Compare commits
115 Commits
85b370ea4c
...
waibao
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b3be8636f | |||
| 5eb9b455a5 | |||
| 4947f085c7 | |||
| 5460aadf6c | |||
| 79a8040446 | |||
| ed36292c94 | |||
| 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 |
@ -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,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));
|
||||
}
|
||||
}
|
||||
@ -7,8 +7,12 @@ 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;
|
||||
@ -19,6 +23,7 @@ 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;
|
||||
@ -49,6 +54,58 @@ public class EmsPointMatchController extends BaseController
|
||||
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
|
||||
@ -85,4 +142,18 @@ public class EmsPointMatchController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据站点导入模板点位配置
|
||||
* @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,37 +1,51 @@
|
||||
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.common.utils.poi.ExcelUtil;
|
||||
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.SiteDeviceListVo;
|
||||
import com.xzzn.ems.mapper.EmsPointMatchMapper;
|
||||
import com.xzzn.ems.domain.vo.SiteMonitorProjectPointMappingSaveRequest;
|
||||
import com.xzzn.ems.domain.vo.SingleBatteryConfigInitRequest;
|
||||
import com.xzzn.ems.domain.vo.SingleBatteryConfigInitResultVo;
|
||||
import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportResultVo;
|
||||
import com.xzzn.ems.domain.vo.SingleBatteryMonitorImportRowVo;
|
||||
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.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
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;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
*
|
||||
* 站点配置
|
||||
*
|
||||
* 绔欑偣閰嶇疆
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/ems/siteConfig")
|
||||
public class EmsSiteConfigController extends BaseController{
|
||||
public class EmsSiteConfigController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private IEmsSiteService iEmsSiteService;
|
||||
@ -39,154 +53,169 @@ public class EmsSiteConfigController extends BaseController{
|
||||
@Autowired
|
||||
private IEmsDeviceSettingService iEmsDeviceSettingService;
|
||||
|
||||
/**
|
||||
* 获取站点列表
|
||||
*/
|
||||
@GetMapping("/getSiteInfoList")
|
||||
public TableDataInfo getSiteInfoList(@RequestParam String siteName, @RequestParam String startTime, @RequestParam String endTime)
|
||||
{
|
||||
public TableDataInfo getSiteInfoList(@RequestParam String siteName, @RequestParam String startTime, @RequestParam String endTime) {
|
||||
startPage();
|
||||
List<EmsSiteSetting> list = iEmsSiteService.getAllSiteInfoList(siteName,startTime,endTime);
|
||||
List<EmsSiteSetting> list = iEmsSiteService.getAllSiteInfoList(siteName, startTime, endTime);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备详细信息
|
||||
*/
|
||||
@GetMapping("/getDeviceDetailInfo")
|
||||
public AjaxResult getDeviceDetailInfo(@RequestParam Long id)
|
||||
{
|
||||
public AjaxResult getDeviceDetailInfo(@RequestParam Long id) {
|
||||
return success(iEmsDeviceSettingService.getDeviceDetailInfo(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备列表-不分页
|
||||
*/
|
||||
@GetMapping("/getDeviceList")
|
||||
public AjaxResult getDeviceInfoList2(@RequestParam String siteId)
|
||||
{
|
||||
return success(iEmsSiteService.getAllDeviceList(siteId));
|
||||
public AjaxResult getDeviceInfoList2(@RequestParam String siteId) {
|
||||
return success(iEmsSiteService.getAllDeviceList(siteId, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有设备类别
|
||||
*/
|
||||
@GetMapping("/getDeviceCategory")
|
||||
public AjaxResult getDeviceCategory()
|
||||
{
|
||||
public AjaxResult getDeviceCategory() {
|
||||
return success(iEmsDeviceSettingService.getDeviceCategory());
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增设备
|
||||
*/
|
||||
@PostMapping("/addDevice")
|
||||
public AjaxResult addDevice(@RequestBody EmsDevicesSetting devicesSetting)
|
||||
{
|
||||
public AjaxResult addDevice(@RequestBody DevicesSettingVo devicesSetting) {
|
||||
int result = iEmsDeviceSettingService.addDevice(devicesSetting);
|
||||
if (result > 0) {
|
||||
if (result > 0) {
|
||||
return AjaxResult.success(result);
|
||||
} else {
|
||||
return AjaxResult.error("该设备已存在");
|
||||
}
|
||||
return AjaxResult.error("璇ヨ澶囧凡瀛樺湪");
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备图片
|
||||
*/
|
||||
@PostMapping("/uploadDeviceImg")
|
||||
public AjaxResult uploadDeviceImg(@RequestParam("avatarfile") MultipartFile file) throws Exception
|
||||
{
|
||||
public AjaxResult uploadDeviceImg(@RequestParam("avatarfile") MultipartFile file) throws Exception {
|
||||
if (!file.isEmpty()) {
|
||||
String avatar = FileUploadUtils.upload(RuoYiConfig.getDevicePath(), file, MimeTypeUtils.IMAGE_EXTENSION);
|
||||
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax.put("imgUrl", avatar);
|
||||
return ajax;
|
||||
}
|
||||
return error("上传图片异常,请联系管理员");
|
||||
return error("涓婁紶鍥剧墖寮傚父锛岃鑱旂郴绠$悊鍛?");
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改Modbus设备配置
|
||||
*/
|
||||
@PostMapping("/updateDevice")
|
||||
public AjaxResult updateDevice(@RequestBody EmsDevicesSetting emsDevicesSetting)
|
||||
{
|
||||
public AjaxResult updateDevice(@RequestBody DevicesSettingVo emsDevicesSetting) {
|
||||
int result = iEmsDeviceSettingService.updateDevice(emsDevicesSetting);
|
||||
if (result > 0) {
|
||||
if (result > 0) {
|
||||
return AjaxResult.success(result);
|
||||
} else if (result == -1) {
|
||||
return AjaxResult.error("数据不存在");
|
||||
} else if (result == -2) {
|
||||
return AjaxResult.error("该设备已存在");
|
||||
} else if (result == -1) {
|
||||
return AjaxResult.error("鏁版嵁涓嶅瓨鍦?");
|
||||
} else if (result == -2) {
|
||||
return AjaxResult.error("璇ヨ澶囧凡瀛樺湪");
|
||||
}
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Modbus设备配置
|
||||
*/
|
||||
@DeleteMapping("/deleteService/{id}")
|
||||
public AjaxResult deleteService(@PathVariable Long id)
|
||||
{
|
||||
public AjaxResult deleteService(@PathVariable Long id) {
|
||||
return toAjax(iEmsDeviceSettingService.deleteEmsDevicesSettingById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个站点单个设备点位查询-点位清单
|
||||
*/
|
||||
@GetMapping("/getDevicePointList")
|
||||
public TableDataInfo getDevicePointList(@Validated PointDataRequest request)
|
||||
{
|
||||
public TableDataInfo getDevicePointList(@Validated PointDataRequest request) {
|
||||
List<PointQueryResponse> result = iEmsDeviceSettingService.getSingleSiteDevicePoints(request);
|
||||
return getDataTable2(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定站点下的所有设备类别
|
||||
*/
|
||||
@GetMapping("/getSiteAllDeviceCategory")
|
||||
public AjaxResult getSiteAllDeviceCategory(String siteId)
|
||||
{
|
||||
public AjaxResult getSiteAllDeviceCategory(String siteId) {
|
||||
return success(iEmsDeviceSettingService.getSiteAllDeviceCategory(siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备类别获取父类的设备id
|
||||
*/
|
||||
@GetMapping("/getParentDeviceId")
|
||||
public AjaxResult getParentDeviceId(@RequestParam String siteId, @RequestParam String deviceCategory)
|
||||
{
|
||||
public AjaxResult getParentDeviceId(@RequestParam String siteId, @RequestParam String deviceCategory) {
|
||||
return success(iEmsSiteService.getParentCategoryDeviceId(siteId, deviceCategory));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定站点下的指定设备类型的设备
|
||||
*/
|
||||
@GetMapping("/getDeviceListBySiteAndCategory")
|
||||
public AjaxResult getDeviceListBySiteAndCategory(String siteId,String deviceCategory)
|
||||
{
|
||||
public AjaxResult getDeviceListBySiteAndCategory(String siteId, String deviceCategory) {
|
||||
return success(iEmsDeviceSettingService.getDeviceListBySiteAndCategory(siteId, deviceCategory));
|
||||
}
|
||||
|
||||
/**
|
||||
* PCS设备开关机
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
@GetMapping("/getSingleMonitorWorkStatusEnumMappings")
|
||||
public AjaxResult getSingleMonitorWorkStatusEnumMappings(@RequestParam String siteId) {
|
||||
return success(iEmsDeviceSettingService.getSiteWorkStatusEnumMappings(siteId));
|
||||
}
|
||||
|
||||
@PostMapping("/saveSingleMonitorWorkStatusEnumMappings")
|
||||
public AjaxResult saveSingleMonitorWorkStatusEnumMappings(@RequestBody WorkStatusEnumMappingSaveRequest request) {
|
||||
int rows = iEmsDeviceSettingService.saveSiteWorkStatusEnumMappings(request.getSiteId(), request.getMappings(), getUsername());
|
||||
return AjaxResult.success(rows);
|
||||
}
|
||||
|
||||
@GetMapping("/downloadSingleBatteryMonitorImportTemplate")
|
||||
public void downloadSingleBatteryMonitorImportTemplate(HttpServletResponse response) {
|
||||
ExcelUtil<SingleBatteryMonitorImportRowVo> util = new ExcelUtil<>(SingleBatteryMonitorImportRowVo.class);
|
||||
util.importTemplateExcel(response, "单体电池导入模板", "单体电池导入模板");
|
||||
}
|
||||
|
||||
@PostMapping("/importSingleBatteryMonitorMappings")
|
||||
public AjaxResult importSingleBatteryMonitorMappings(@RequestParam("siteId") String siteId,
|
||||
@RequestParam("file") MultipartFile file) throws Exception {
|
||||
ExcelUtil<SingleBatteryMonitorImportRowVo> util = new ExcelUtil<>(SingleBatteryMonitorImportRowVo.class);
|
||||
List<SingleBatteryMonitorImportRowVo> rowList = util.importExcel(file.getInputStream(), 1);
|
||||
if (rowList == null) {
|
||||
return AjaxResult.error("导入失败:未读取到任何数据,请检查 Excel 文件内容");
|
||||
}
|
||||
List<SingleBatteryMonitorImportRowVo> validRowList = new ArrayList<>();
|
||||
for (int i = 0; i < rowList.size(); i++) {
|
||||
SingleBatteryMonitorImportRowVo row = rowList.get(i);
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
row.setRowNum(i + 3);
|
||||
validRowList.add(row);
|
||||
}
|
||||
if (validRowList.isEmpty()) {
|
||||
return AjaxResult.error("导入失败:未识别到有效数据,请使用系统导出的最新模板,并保持表头名称不变");
|
||||
}
|
||||
SingleBatteryMonitorImportResultVo result = iEmsDeviceSettingService.importSingleBatteryMonitorMappings(siteId, validRowList, getUsername());
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
@PostMapping("/initializeSingleBatteryMonitorMappings")
|
||||
public AjaxResult initializeSingleBatteryMonitorMappings(@RequestBody SingleBatteryConfigInitRequest request) {
|
||||
SingleBatteryConfigInitResultVo result = iEmsDeviceSettingService.initializeSingleBatteryMonitorMappings(request, getUsername());
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
// @PreAuthorize("@ss.hasPermi('system:device:onAndOff')")
|
||||
@Log(title = "寮€鍏虫満", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/updateDeviceStatus")
|
||||
public AjaxResult updateDeviceStatus(@Valid @RequestBody DeviceUpdateRequest request)
|
||||
{
|
||||
public AjaxResult updateDeviceStatus(@Valid @RequestBody DeviceUpdateRequest request) {
|
||||
return success(iEmsDeviceSettingService.updateDeviceStatus(request));
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,9 +9,14 @@ 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.*;
|
||||
|
||||
@ -26,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;
|
||||
@ -33,6 +40,8 @@ public class EmsSiteMonitorController extends BaseController{
|
||||
private IEmsSiteService iEmsSiteService;
|
||||
@Autowired
|
||||
private IEmsStatsReportService iemsStatsReportService;
|
||||
@Autowired
|
||||
private IEmsDeviceSettingService iEmsDeviceSettingService;
|
||||
|
||||
/**
|
||||
* 获取单站首页数据
|
||||
@ -43,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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单站监控-设备监控-实时运行头部数据
|
||||
*/
|
||||
@ -56,27 +74,75 @@ public class EmsSiteMonitorController extends BaseController{
|
||||
* 单站监控-设备监控-实时运行曲线图数据
|
||||
*/
|
||||
@GetMapping("/runningGraph/storagePower")
|
||||
public AjaxResult getRunningGraphStorage(RunningGraphRequest request)
|
||||
public AjaxResult getRunningGraphStorage(RunningGraphRequest request,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate)
|
||||
{
|
||||
return success(iSingleSiteService.getRunningGraphStorage(request));
|
||||
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/pcsMaxTemp")
|
||||
public AjaxResult getRunningGraphPcsMaxTemp(RunningGraphRequest request)
|
||||
public AjaxResult getRunningGraphPcsMaxTemp(RunningGraphRequest request,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate)
|
||||
{
|
||||
return success(iSingleSiteService.getRunningGraphPcsMaxTemp(request));
|
||||
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(RunningGraphRequest request)
|
||||
public AjaxResult getRunningGraphBatterySoc(RunningGraphRequest request,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate)
|
||||
{
|
||||
return success(iSingleSiteService.getRunningGraphBatterySoc(request));
|
||||
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(RunningGraphRequest request)
|
||||
public AjaxResult getRunningGraphBatteryTemp(RunningGraphRequest request,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate)
|
||||
{
|
||||
return success(iSingleSiteService.getRunningGraphBatteryTemp(request));
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,9 +217,7 @@ public class EmsSiteMonitorController extends BaseController{
|
||||
{
|
||||
startPage();
|
||||
SiteBatteryDataList siteBatteryDataList = new SiteBatteryDataList();
|
||||
// 簇最大最小单体id数据
|
||||
List<BMSBatteryDataList> clusterBatteryDataList = iSingleSiteService.getClusterBatteryList(siteId,stackDeviceId,clusterDeviceId);
|
||||
siteBatteryDataList.setClusterList(clusterBatteryDataList);
|
||||
|
||||
// 单体电池数据
|
||||
List<BatteryDataStatsListVo> List = iSingleSiteService.getClusterDataInfoList(clusterDeviceId,siteId,stackDeviceId,batteryId);
|
||||
// 对batteryList进行分页处理
|
||||
@ -228,4 +292,31 @@ public class EmsSiteMonitorController extends BaseController{
|
||||
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,19 +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.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 单站监控-统计报表
|
||||
*
|
||||
@ -26,6 +40,8 @@ public class EmsStatisticalReportController extends BaseController
|
||||
|
||||
@Autowired
|
||||
private IEmsStatsReportService ieEmsStatsReportService;
|
||||
@Autowired
|
||||
private IEmsWeatherSyncService iEmsWeatherSyncService;
|
||||
|
||||
/**
|
||||
* 概率统计-收益指标查询
|
||||
@ -118,6 +134,39 @@ public class EmsStatisticalReportController extends BaseController
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 概率统计-电表收益报表
|
||||
*/
|
||||
@ -129,6 +178,17 @@ public class EmsStatisticalReportController extends BaseController
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 概率统计-功率曲线
|
||||
*/
|
||||
@ -142,4 +202,19 @@ public class EmsStatisticalReportController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动触发天气同步
|
||||
*/
|
||||
@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,61 +1,52 @@
|
||||
package com.xzzn.web.controller.ems;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
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.domain.MqttSyncLog;
|
||||
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||
import com.xzzn.ems.service.*;
|
||||
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 java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
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;
|
||||
import java.util.List;
|
||||
|
||||
@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;
|
||||
|
||||
@Autowired
|
||||
private IDDSDataProcessService dDSDataProcessService;
|
||||
|
||||
@Autowired
|
||||
private IDeviceDataProcessService deviceDataProcessService;
|
||||
|
||||
@Autowired
|
||||
private IFXXAlarmDataProcessService fXXAlarmDataProcessService;
|
||||
@Autowired
|
||||
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||
@Autowired
|
||||
private IEmsStrategyService emsStrategyService;
|
||||
@Autowired
|
||||
private IMqttSyncLogService iMqttSyncLogService;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
@Autowired
|
||||
public MqttMessageController(MqttLifecycleManager mqttLifecycleManager) {
|
||||
@ -131,50 +122,32 @@ public class MqttMessageController implements MqttPublisher, MqttSubscriber {
|
||||
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());
|
||||
log.info("[DEVICE] data: " + payload);
|
||||
try {
|
||||
// 业务处理逻辑
|
||||
// if (topic.startsWith("021_DDS")) {
|
||||
// dDSDataProcessService.handleDdsData(payload);
|
||||
// } else if (topic.startsWith("021_FXX")) {
|
||||
// fXXDataProcessService.handleFxData(payload);
|
||||
// }
|
||||
deviceDataProcessService.handleDeviceData(payload, getSiteIdByTopic(topic));
|
||||
|
||||
emsMqttMessageService.insertMqttOriginalMessage(topic, payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process device data message: " + e.getMessage(), e);
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 处理告警数据
|
||||
private void handleAlarmData(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
System.out.println("[DEVICE] data: " + payload);
|
||||
try {
|
||||
// 业务处理逻辑
|
||||
// if (topic.startsWith("021_FXX")) {
|
||||
// fXXAlarmDataProcessService.handleFxAlarmData(payload);
|
||||
// }
|
||||
deviceDataProcessService.handleAlarmData(payload, getSiteIdByTopic(topic));
|
||||
|
||||
emsMqttMessageService.insertMqttOriginalMessage(topic, payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process device alarm data message: " + e.getMessage(), e);
|
||||
}
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
try {
|
||||
deviceDataProcessService.handleAlarmData(payload, getSiteIdByTopic(topic));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process device alarm data message: {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@ -185,78 +158,67 @@ public class MqttMessageController implements MqttPublisher, MqttSubscriber {
|
||||
siteId = topicConfig.getSiteId();
|
||||
redisCache.setCacheObject(RedisKeyConstants.SITE_ID + topic, siteId);
|
||||
}
|
||||
log.info("当前处理数据站点:" + siteId + ",topic: " + topic);
|
||||
return siteId;
|
||||
}
|
||||
|
||||
// 处理运行策略数据:云端-本地
|
||||
private void handleStrategyData(String topic, MqttMessage message) {
|
||||
String payload = new String(message.getPayload());
|
||||
System.out.println("[处理运行策略数据] data: " + payload);
|
||||
try {
|
||||
// 业务处理逻辑
|
||||
iMqttSyncLogService.handleMqttStrategyData(payload);
|
||||
|
||||
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process strategy data message: " + e.getMessage(), e);
|
||||
}
|
||||
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());
|
||||
System.out.println("[处理设备保护告警策略数据] data: " + payload);
|
||||
try {
|
||||
// 业务处理逻辑
|
||||
iMqttSyncLogService.handleMqttPlanData(payload);
|
||||
|
||||
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process fault plan data message: " + e.getMessage(), e);
|
||||
}
|
||||
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());
|
||||
System.out.println("[处理本地保护策略告警信息到云端] data: " + payload);
|
||||
try {
|
||||
// 业务处理逻辑
|
||||
iMqttSyncLogService.handleFaultAlarmData(payload);
|
||||
|
||||
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process fault plan alarm data message: " + e.getMessage(), e);
|
||||
}
|
||||
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());
|
||||
System.out.println("[处理本地保护策略下发日志到云端] data: " + payload);
|
||||
try {
|
||||
// 业务处理逻辑
|
||||
iMqttSyncLogService.handleFaultPlanIssueData(payload);
|
||||
|
||||
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process fault plan issue log message: " + e.getMessage(), e);
|
||||
}
|
||||
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());
|
||||
System.out.println("[处理本地的保护策略告警信息到云端] data: " + payload);
|
||||
try {
|
||||
// 业务处理逻辑
|
||||
iMqttSyncLogService.handleDeviceChangeLogData(payload);
|
||||
|
||||
emsMqttMessageService.insertMqttOriginalMessage(topic,payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process device change log message: " + e.getMessage(), e);
|
||||
}
|
||||
threadPoolTaskExecutor.execute(() -> {
|
||||
try {
|
||||
iMqttSyncLogService.handleDeviceChangeLogData(payload);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process device change log message: {}", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -286,4 +248,4 @@ public class MqttMessageController implements MqttPublisher, MqttSubscriber {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package com.xzzn.web.controller.system;
|
||||
|
||||
import com.xzzn.common.core.controller.BaseController;
|
||||
import com.xzzn.common.core.domain.AjaxResult;
|
||||
import com.xzzn.ems.domain.vo.BizRemarkBatchGetRequest;
|
||||
import com.xzzn.ems.domain.vo.BizRemarkSaveRequest;
|
||||
import com.xzzn.ems.service.ISysBizRemarkService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/system/bizRemark")
|
||||
public class SysBizRemarkController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ISysBizRemarkService sysBizRemarkService;
|
||||
|
||||
@PostMapping("/batchGet")
|
||||
public AjaxResult batchGet(@RequestBody BizRemarkBatchGetRequest request)
|
||||
{
|
||||
return success(sysBizRemarkService.getRemarkMap(request.getBizType(), request.getBizKey1(), request.getBizKey2List()));
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
public AjaxResult save(@RequestBody BizRemarkSaveRequest request)
|
||||
{
|
||||
return toAjax(sysBizRemarkService.saveRemark(request, getUsername()));
|
||||
}
|
||||
}
|
||||
@ -194,6 +194,8 @@ mqtt:
|
||||
connection-timeout: 15
|
||||
keep-alive-interval: 30
|
||||
automatic-reconnect: true
|
||||
topic: 021_DDS_01_UP
|
||||
siteId: 021_DDS_01
|
||||
|
||||
modbus:
|
||||
pool:
|
||||
@ -203,3 +205,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
|
||||
|
||||
@ -194,6 +194,8 @@ mqtt:
|
||||
connection-timeout: 15
|
||||
keep-alive-interval: 30
|
||||
automatic-reconnect: true
|
||||
topic:
|
||||
siteId:
|
||||
|
||||
modbus:
|
||||
pool:
|
||||
@ -203,3 +205,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
|
||||
|
||||
@ -196,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:
|
||||
@ -205,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="../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" />
|
||||
|
||||
@ -90,4 +90,4 @@
|
||||
<logger name="sys-user" level="info">
|
||||
<appender-ref ref="sys-user"/>
|
||||
</logger>
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
||||
@ -119,6 +119,12 @@
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- modbus4j (保留兼容) -->
|
||||
<dependency>
|
||||
<groupId>com.infiniteautomation</groupId>
|
||||
<artifactId>modbus4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@ -96,9 +96,16 @@ public class RedisKeyConstants
|
||||
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";
|
||||
|
||||
@ -118,4 +125,19 @@ public class RedisKeyConstants
|
||||
|
||||
/** 每个设备最新数据-设置失效时间-判断是否正常同步数据 */
|
||||
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_";
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,5 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* device-通信状态
|
||||
*
|
||||
@ -11,10 +8,7 @@ import java.util.List;
|
||||
public enum DeviceRunningStatus
|
||||
{
|
||||
OFFLINE("0", "离线"),
|
||||
STANDBY("1", "待机") ,
|
||||
RUNNING("2", "运行"),
|
||||
FAULT("3", "故障"),
|
||||
SHUTDOWN("4", "停机");
|
||||
ONLINE("1", "在线");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
@ -35,15 +29,4 @@ public enum DeviceRunningStatus
|
||||
return info;
|
||||
}
|
||||
|
||||
/** 停机状态 */
|
||||
public static List<String> getShutdownCodeList()
|
||||
{
|
||||
return Arrays.asList(SHUTDOWN.getCode());
|
||||
}
|
||||
|
||||
/** 运行状态 */
|
||||
public static List<String> getRunningCodeList()
|
||||
{
|
||||
return Arrays.asList(STANDBY.getCode(), RUNNING.getCode(), FAULT.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,8 @@
|
||||
package com.xzzn.common.enums;
|
||||
|
||||
public enum RegisterType {
|
||||
COIL,
|
||||
DISCRETE_INPUT,
|
||||
HOLDING_REGISTER,
|
||||
INPUT_REGISTER
|
||||
}
|
||||
@ -50,6 +50,11 @@ public enum SiteDevice
|
||||
*/
|
||||
DH,
|
||||
|
||||
/**
|
||||
* 消防
|
||||
*/
|
||||
XF,
|
||||
|
||||
/**
|
||||
* 中水冷却
|
||||
*/
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
@ -5,11 +5,11 @@ import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DataUtils {
|
||||
private static final Log log = LogFactory.getLog(DataUtils.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(DataUtils.class);
|
||||
|
||||
public static String getJSONFromFile(InputStream inputStream) {
|
||||
BufferedReader bufferReader = null;
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
package com.xzzn.common.utils;
|
||||
|
||||
import com.xzzn.common.annotation.Log;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -161,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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算时间差
|
||||
*
|
||||
@ -410,7 +416,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
||||
// 减去一天得到昨天
|
||||
LocalDate yesterday = today.minusDays(1);
|
||||
// 定义日期格式化器
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YYYYMMDD);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YYYY_MM_DD);
|
||||
// 格式化并返回
|
||||
return yesterday.format(formatter);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -10,18 +10,23 @@ 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 org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
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;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 策略运行切面同步
|
||||
* 云端 - 本地
|
||||
@ -30,7 +35,7 @@ import java.util.*;
|
||||
@Component
|
||||
public class StrategyRunningSyncAspect {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(StrategyRunningSyncAspect.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(StrategyRunningSyncAspect.class);
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
|
||||
@ -4,23 +4,34 @@ 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.*;
|
||||
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.*;
|
||||
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 org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
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;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 策略模板数据同步
|
||||
* 云端 - 本地
|
||||
@ -29,7 +40,7 @@ import java.util.*;
|
||||
@Component
|
||||
public class StrategyTempSyncAspect {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(StrategyTempSyncAspect.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(StrategyTempSyncAspect.class);
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
@ -47,7 +58,7 @@ public class StrategyTempSyncAspect {
|
||||
|
||||
// 用ThreadLocal暂存删除前的对象
|
||||
private ThreadLocal<EmsStrategyTemp> beforeDeleteThreadLocal = new ThreadLocal<>();
|
||||
@Before("execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.deleteEmsStrategyTempById(..)) && args(templateId)")
|
||||
@Before("execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.deleteTempByTempId(..)) && args(templateId)")
|
||||
public void beforeDelete(JoinPoint joinPoint, String templateId) {
|
||||
// 查询删除前的数据-仅存一获取siteId
|
||||
List<EmsStrategyTemp> tempList = emsStrategyTempMapper.selectStrategyTempByTempId(templateId);
|
||||
@ -56,7 +67,7 @@ public class StrategyTempSyncAspect {
|
||||
}
|
||||
}
|
||||
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.deleteEmsStrategyTempById(..)) && args(templateId)) ")
|
||||
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsStrategyTempMapper.deleteTempByTempId(..)) && args(templateId)) ")
|
||||
public void deletePointCut(String templateId) {
|
||||
logger.info("【删除策略模版切面】StrategyTempSyncAspect 被实例化");
|
||||
}
|
||||
@ -94,7 +105,7 @@ public class StrategyTempSyncAspect {
|
||||
}
|
||||
// 数据转换
|
||||
SyncStrategyTempVo tempVo = convertEntity(insertEntity);
|
||||
String content = objectMapper.writeValueAsString(tempVo);
|
||||
String content = JSON.toJSONString(tempVo);
|
||||
message.setContent(content);
|
||||
|
||||
// 发布到MQTT主题
|
||||
|
||||
@ -3,22 +3,32 @@ 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.*;
|
||||
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.*;
|
||||
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 org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
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;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 策略时间配置同步
|
||||
* 云端 - 本地
|
||||
@ -27,7 +37,7 @@ import java.util.*;
|
||||
@Component
|
||||
public class StrategyTimeConfigSyncAspect {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(StrategyTimeConfigSyncAspect.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(StrategyTimeConfigSyncAspect.class);
|
||||
@Autowired
|
||||
private MqttPublisher mqttPublisher;
|
||||
|
||||
|
||||
@ -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,274 +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 com.xzzn.ems.service.IEmsAlarmRecordsService;
|
||||
import com.xzzn.ems.service.IEmsDeviceSettingService;
|
||||
import com.xzzn.ems.service.IEmsEnergyPriceConfigService;
|
||||
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;
|
||||
@Autowired
|
||||
private IEmsAlarmRecordsService iEmsAlarmRecordsService;
|
||||
@Autowired
|
||||
private IEmsEnergyPriceConfigService iEmsEnergyPriceConfigService;
|
||||
@Autowired
|
||||
private IEmsDeviceSettingService iEmsDeviceSettingService;
|
||||
|
||||
@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连接管理器已初始化");
|
||||
|
||||
// 初始数据工作
|
||||
initData();
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
// 初始化-设备信息
|
||||
iEmsDeviceSettingService.initDeviceInfo();
|
||||
// 初始化-告警数据
|
||||
iEmsAlarmRecordsService.initAlarmMatchInfo();
|
||||
// 初始化当月电价
|
||||
iEmsEnergyPriceConfigService.initCurrentMonthPrice();
|
||||
}
|
||||
/**
|
||||
* 获取连接(带自动创建和缓存)
|
||||
*/
|
||||
|
||||
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("10.1.0.230");
|
||||
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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -3,21 +3,37 @@ 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;
|
||||
|
||||
@ -41,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,
|
||||
@ -51,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;
|
||||
}
|
||||
|
||||
@ -83,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
|
||||
@ -106,14 +133,16 @@ 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);
|
||||
}
|
||||
|
||||
@ -135,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;
|
||||
@ -159,4 +228,4 @@ public class MqttLifecycleManager implements ApplicationRunner, SmartLifecycle,
|
||||
return qos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,279 +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.ModbusSerialTransaction;
|
||||
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.msg.WriteMultipleRegistersRequest;
|
||||
import com.ghgande.j2mod.modbus.msg.WriteSingleRegisterRequest;
|
||||
import com.ghgande.j2mod.modbus.net.SerialConnection;
|
||||
import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
|
||||
import com.ghgande.j2mod.modbus.procimg.Register;
|
||||
import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
|
||||
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) throws ModbusException {
|
||||
if (!connection.isOpen()) {
|
||||
throw new ModbusIOException("RTU连接未建立");
|
||||
}
|
||||
ReadInputRegistersRequest request = new ReadInputRegistersRequest(startAddr, count);
|
||||
ModbusSerialTransaction transaction = new ModbusSerialTransaction(connection);
|
||||
transaction.setRequest(request);
|
||||
transaction.setRetries(2);
|
||||
|
||||
try {
|
||||
transaction.execute();
|
||||
ReadInputRegistersResponse response = (ReadInputRegistersResponse) transaction.getResponse();
|
||||
if (response == null) {
|
||||
throw new ModbusException("RTU响应为空");
|
||||
}
|
||||
return parseRegisters(response);
|
||||
} catch (ModbusException e) {
|
||||
logger.error("读取RTU寄存器失败: {}", e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入单个寄存器(支持TCP/RTU)
|
||||
* @param connection 连接对象(TCPMasterConnection 或 SerialConnection)
|
||||
* @param registerAddr 寄存器地址
|
||||
* @param value 要写入的值(16位整数)
|
||||
*/
|
||||
@Retryable(
|
||||
value = {ModbusException.class},
|
||||
maxAttempts = 3,
|
||||
backoff = @Backoff(delay = 1000, multiplier = 2)
|
||||
)
|
||||
@CircuitBreaker(name = "modbusOperation", fallbackMethod = "writeRegisterFallback")
|
||||
public boolean writeSingleRegister(Object connection, int registerAddr, int value) throws ModbusException {
|
||||
try {
|
||||
if (connection instanceof TCPMasterConnection) {
|
||||
return writeTcpSingleRegister((TCPMasterConnection) connection, registerAddr, value);
|
||||
} else if (connection instanceof SerialConnection) {
|
||||
return writeRtuSingleRegister((SerialConnection) connection, registerAddr, value);
|
||||
}
|
||||
throw new IllegalArgumentException("不支持的连接类型: " + connection.getClass().getName());
|
||||
} catch (ModbusIOException e) {
|
||||
throw new ModbusException("写入通信故障", e);
|
||||
} catch (Exception e) {
|
||||
throw new ModbusException("写入系统错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入多个寄存器(支持TCP/RTU)
|
||||
* @param connection 连接对象
|
||||
* @param startAddr 起始寄存器地址
|
||||
* @param values 要写入的值数组(每个值为16位整数)
|
||||
*/
|
||||
@Retryable(
|
||||
value = {ModbusException.class},
|
||||
maxAttempts = 3,
|
||||
backoff = @Backoff(delay = 1000, multiplier = 2)
|
||||
)
|
||||
@CircuitBreaker(name = "modbusOperation", fallbackMethod = "writeRegisterFallback")
|
||||
public boolean writeMultipleRegisters(Object connection, int startAddr, int[] values) throws ModbusException {
|
||||
try {
|
||||
if (connection instanceof TCPMasterConnection) {
|
||||
return writeTcpMultipleRegisters((TCPMasterConnection) connection, startAddr, values);
|
||||
} else if (connection instanceof SerialConnection) {
|
||||
return writeRtuMultipleRegisters((SerialConnection) connection, startAddr, values);
|
||||
}
|
||||
throw new IllegalArgumentException("不支持的连接类型: " + connection.getClass().getName());
|
||||
} catch (ModbusIOException e) {
|
||||
throw new ModbusException("写入通信故障", e);
|
||||
} catch (Exception e) {
|
||||
throw new ModbusException("写入系统错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== TCP写入实现 ====================
|
||||
private boolean writeTcpSingleRegister(TCPMasterConnection conn, int registerAddr, int value) throws ModbusException {
|
||||
if (!conn.isConnected()) {
|
||||
throw new ModbusIOException("TCP连接未建立,无法写入");
|
||||
}
|
||||
// 创建写入单个寄存器的请求(功能码06)
|
||||
WriteSingleRegisterRequest request = new WriteSingleRegisterRequest(registerAddr, new SimpleRegister(value));
|
||||
ModbusTCPTransaction transaction = new ModbusTCPTransaction(conn);
|
||||
transaction.setRequest(request);
|
||||
transaction.setRetries(2);
|
||||
|
||||
try {
|
||||
transaction.execute();
|
||||
logger.info("TCP写入单个寄存器成功,地址:{},值:{}", registerAddr, value);
|
||||
return true;
|
||||
} catch (ModbusException e) {
|
||||
logger.error("TCP写入单个寄存器失败,地址:{},值:{}", registerAddr, value, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeTcpMultipleRegisters(TCPMasterConnection conn, int startAddr, int[] values) throws ModbusException {
|
||||
if (!conn.isConnected()) {
|
||||
throw new ModbusIOException("TCP连接未建立,无法写入");
|
||||
}
|
||||
// 转换值数组为寄存器数组
|
||||
Register[] registers = new Register[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
registers[i] = new SimpleRegister(values[i]);
|
||||
}
|
||||
// 创建写入多个寄存器的请求(功能码16)
|
||||
WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(startAddr, registers);
|
||||
ModbusTCPTransaction transaction = new ModbusTCPTransaction(conn);
|
||||
transaction.setRequest(request);
|
||||
transaction.setRetries(2);
|
||||
|
||||
try {
|
||||
transaction.execute();
|
||||
logger.info("TCP写入多个寄存器成功,起始地址:{},数量:{}", startAddr, values.length);
|
||||
return true;
|
||||
} catch (ModbusException e) {
|
||||
logger.error("TCP写入多个寄存器失败,起始地址:{}", startAddr, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ==================== RTU写入实现 ====================
|
||||
private boolean writeRtuSingleRegister(SerialConnection connection, int registerAddr, int value) throws ModbusException {
|
||||
if (!connection.isOpen()) {
|
||||
throw new ModbusIOException("RTU串口未打开,请先建立连接");
|
||||
}
|
||||
WriteSingleRegisterRequest request = new WriteSingleRegisterRequest(registerAddr, new SimpleRegister(value));
|
||||
ModbusSerialTransaction transaction = new ModbusSerialTransaction(connection);
|
||||
transaction.setRequest(request);
|
||||
transaction.setRetries(2);
|
||||
|
||||
try {
|
||||
transaction.execute();
|
||||
logger.info("RTU写入单个寄存器成功,地址:{},值:{}", registerAddr, value);
|
||||
return true;
|
||||
} catch (ModbusException e) {
|
||||
logger.error("RTU写入单个寄存器失败,地址:{},值:{}", registerAddr, value, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeRtuMultipleRegisters(SerialConnection connection, int startAddr, int[] values) throws ModbusException {
|
||||
if (!connection.isOpen()) {
|
||||
throw new ModbusIOException("RTU串口未打开,请先建立连接");
|
||||
}
|
||||
Register[] registers = new Register[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
registers[i] = new SimpleRegister(values[i]);
|
||||
}
|
||||
WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(startAddr, registers);
|
||||
ModbusSerialTransaction transaction = new ModbusSerialTransaction(connection);
|
||||
transaction.setRequest(request);
|
||||
transaction.setRetries(2);
|
||||
|
||||
try {
|
||||
transaction.execute();
|
||||
logger.info("RTU写入多个寄存器成功,起始地址:{},数量:{}", startAddr, values.length);
|
||||
return true;
|
||||
} catch (ModbusException e) {
|
||||
logger.error("RTU写入多个寄存器失败,起始地址:{}", startAddr, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ==================== 写入操作的降级方法 ====================
|
||||
public boolean writeRegisterFallback(Object connection, int addr, int value, Exception e) {
|
||||
logger.warn("写入单个寄存器降级(原因: {}),地址:{}", e.getMessage(), addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean writeRegisterFallback(Object connection, int startAddr, int[] values, Exception e) {
|
||||
logger.warn("写入多个寄存器降级(原因: {}),起始地址:{}", e.getMessage(), startAddr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -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("创建表结构异常");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改保存代码生成业务
|
||||
*/
|
||||
@ -260,4 +217,4 @@ public class GenController extends BaseController
|
||||
response.setContentType("application/octet-stream; charset=UTF-8");
|
||||
IOUtils.write(data, response.getOutputStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入表结构
|
||||
*
|
||||
@ -528,4 +516,4 @@ public class GenTableServiceImpl implements IGenTableService
|
||||
}
|
||||
return genPath + File.separator + VelocityUtils.getFileName(template, table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
@ -207,4 +203,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -1,179 +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.ems.domain.EmsDeviceChangeLog;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
||||
import com.xzzn.ems.mapper.EmsDeviceChangeLogMapper;
|
||||
import com.xzzn.ems.domain.EmsPointConfig;
|
||||
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
||||
import com.xzzn.ems.mapper.EmsMqttMessageMapper;
|
||||
import com.xzzn.ems.service.impl.EmsDeviceSettingServiceImpl;
|
||||
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 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 java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* 轮询设备-通过modbus协议读取数据
|
||||
*/
|
||||
@Component("modbusPoller")
|
||||
public class ModbusPoller {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ModbusPoller.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(ModbusPoller.class);
|
||||
private static final int SITE_DEVICE_OFFLINE_THRESHOLD = 6;
|
||||
|
||||
private final MqttLifecycleManager mqttLifecycleManager;
|
||||
private final Map<String, Integer> deviceFailureCounts = new ConcurrentHashMap<>();
|
||||
private final AtomicBoolean polling = new AtomicBoolean(false);
|
||||
|
||||
@Resource(name = "modbusExecutor")
|
||||
private ExecutorService modbusExecutor;
|
||||
|
||||
@Autowired
|
||||
private ModbusConnectionManager connectionManager;
|
||||
private ModbusProcessor modbusProcessor;
|
||||
@Autowired
|
||||
private ModbusService modbusService;
|
||||
@Autowired
|
||||
private EmsDevicesSettingMapper deviceRepo;
|
||||
@Autowired
|
||||
private EmsMqttMessageMapper emsMqttMessageMapper;
|
||||
@Autowired
|
||||
private EmsDeviceSettingServiceImpl emsDeviceSettingServiceImpl;
|
||||
@Autowired
|
||||
private EmsDeviceChangeLogMapper emsDeviceChangeLogMapper;
|
||||
private IEmsAlarmRecordsService iEmsAlarmRecordsService;
|
||||
|
||||
@Autowired
|
||||
public ModbusPoller(MqttLifecycleManager mqttLifecycleManager) {
|
||||
this.mqttLifecycleManager = mqttLifecycleManager;
|
||||
}
|
||||
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() {
|
||||
logger.info("开始执行Modbus设备轮询...");
|
||||
EmsDevicesSetting selectEntity = new EmsDevicesSetting();
|
||||
selectEntity.setDeviceStatus(DeviceRunningStatus.RUNNING.getCode());
|
||||
List<EmsDevicesSetting> activeDevices = deviceRepo.selectEmsDevicesSettingList(selectEntity);
|
||||
|
||||
EmsDevicesSetting device = activeDevices.get(0);
|
||||
try {
|
||||
//pollSingleDevice(device);
|
||||
} catch (Exception e) {
|
||||
logger.error("调度设备{}任务失败", device.getId(), e);
|
||||
if (!polling.compareAndSet(false, true)) {
|
||||
log.warn("上一次轮询尚未完成,本次轮询跳过");
|
||||
return;
|
||||
}
|
||||
/*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);
|
||||
|
||||
if(wrapper == null || !wrapper.isActive()){
|
||||
logger.error("轮询设备{}连接失败: {}", device.getId());
|
||||
return;
|
||||
}
|
||||
// 读取保持寄存器
|
||||
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()));
|
||||
}
|
||||
|
||||
// 设备轮询不到修改运行状态
|
||||
String beforeStatus = device.getDeviceStatus();
|
||||
device.setDeviceStatus(DeviceRunningStatus.SHUTDOWN.getCode());
|
||||
emsDeviceSettingServiceImpl.updateDevice(device);
|
||||
|
||||
// 轮询设备,设备状态变更日志
|
||||
EmsDeviceChangeLog log = createLogEntity(beforeStatus,device);
|
||||
emsDeviceChangeLogMapper.insertEmsDeviceChangeLog(log);
|
||||
|
||||
throw new RuntimeException("轮询设备失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理获取到的数据
|
||||
private void processData(EmsDevicesSetting device, int[] data) throws MqttException {
|
||||
String beforeStatus = device.getDeviceStatus();
|
||||
Boolean error = true;
|
||||
if (data == null || data.length == 0) {
|
||||
logger.warn("设备{}返回空数据", device.getId());
|
||||
// 设备读取不到-设置设备故障
|
||||
device.setDeviceStatus(DeviceRunningStatus.FAULT.getCode());
|
||||
error = false;
|
||||
} else {
|
||||
// 恢复设备状态 - 运行
|
||||
device.setDeviceStatus(DeviceRunningStatus.RUNNING.getCode());
|
||||
}
|
||||
emsDeviceSettingServiceImpl.updateDevice(device);
|
||||
// 轮询设备,设备状态变更日志
|
||||
EmsDeviceChangeLog log = createLogEntity(beforeStatus,device);
|
||||
emsDeviceChangeLogMapper.insertEmsDeviceChangeLog(log);
|
||||
// 错误数据-不处理直接返回
|
||||
if (!error) {
|
||||
List<PollingTask> pollingTasks = buildPollingTasks();
|
||||
if (CollectionUtils.isEmpty(pollingTasks)) {
|
||||
log.warn("未查询到可用的Modbus采集点位配置,跳过本轮轮询");
|
||||
polling.set(false);
|
||||
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(" ");
|
||||
// 按主机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();
|
||||
}
|
||||
|
||||
String message = sb.toString();
|
||||
logger.info(sb.toString());
|
||||
/*
|
||||
String siteId = device.getSiteId();
|
||||
if (siteId.startsWith("021_DDS")) {
|
||||
dDSDataProcessService.handleDdsData(message);
|
||||
} else if (siteId.startsWith("021_FXX")) {
|
||||
fXXDataProcessService.handleFxData(message);
|
||||
}*/
|
||||
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));
|
||||
|
||||
// 测试发送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);*/
|
||||
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 EmsDeviceChangeLog createLogEntity(String beforeStatus, EmsDevicesSetting device) {
|
||||
EmsDeviceChangeLog log = new EmsDeviceChangeLog();
|
||||
log.setLogId(UUID.randomUUID().toString());
|
||||
log.setLogTime(new Date());
|
||||
log.setSiteId(device.getSiteId());
|
||||
log.setDeviceId(device.getDeviceId());
|
||||
log.setBeforeStatus(beforeStatus);
|
||||
log.setAfterStatus(device.getDeviceStatus());
|
||||
log.setCreateBy("sys");
|
||||
log.setCreateTime(new Date());
|
||||
return log;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,16 +10,17 @@ 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.enums.StrategyStatus;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import com.xzzn.ems.domain.*;
|
||||
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.mapper.*;
|
||||
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 com.xzzn.framework.manager.ModbusConnectionManager;
|
||||
import com.xzzn.framework.manager.ModbusConnectionWrapper;
|
||||
import com.xzzn.framework.web.service.ModbusService;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -28,21 +29,27 @@ import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
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.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 告警保护方案轮询
|
||||
*
|
||||
* @author xzzn
|
||||
*/
|
||||
@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
|
||||
@ -50,20 +57,11 @@ public class ProtectionPlanTask {
|
||||
@Autowired
|
||||
private EmsAlarmRecordsMapper emsAlarmRecordsMapper;
|
||||
@Autowired
|
||||
private EmsStrategyRunningMapper emsStrategyRunningMapper;
|
||||
@Autowired
|
||||
private EmsFaultProtectionPlanMapper emsFaultProtectionPlanMapper;
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
@Autowired
|
||||
private EmsDevicesSettingMapper emsDevicesSettingMapper;
|
||||
@Autowired
|
||||
private ModbusConnectionManager connectionManager;
|
||||
@Autowired
|
||||
private ModbusService modbusService;
|
||||
@Autowired
|
||||
private EmsFaultIssueLogMapper emsFaultIssueLogMapper;
|
||||
|
||||
public ProtectionPlanTask(IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService) {
|
||||
this.iEmsFaultProtectionPlanService = iEmsFaultProtectionPlanService;
|
||||
@ -72,273 +70,456 @@ public class ProtectionPlanTask {
|
||||
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)) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
// 保护前提
|
||||
String protectionSettings = plan.getProtectionSettings();
|
||||
final List<ProtectionSettingVo> protSettings = objectMapper.readValue(
|
||||
protectionSettings,
|
||||
new TypeReference<List<ProtectionSettingVo>>() {}
|
||||
);
|
||||
if (protSettings == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理告警保护方案
|
||||
boolean isHighLevel = dealWithProtectionPlan(plan, protSettings);
|
||||
if (isHighLevel) {
|
||||
// 触发最高故障等级-结束循环
|
||||
return;
|
||||
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 boolean dealWithProtectionPlan(EmsFaultProtectionPlan plan, List<ProtectionSettingVo> protSettings) {
|
||||
boolean isHighLevel = false;
|
||||
|
||||
private void dealWithProtectionPlan(EmsFaultProtectionPlan plan, ProtectionSettingsGroupVo settingGroup) {
|
||||
logger.info("<轮询保护方案> 站点:{},方案ID:{}", plan.getSiteId(), plan.getId());
|
||||
String siteId = plan.getSiteId();
|
||||
final Integer isAlertAlarm = plan.getIsAlert();
|
||||
final Long status = plan.getStatus();
|
||||
// 看方案是否启用,走不同判断
|
||||
if (status == ProtPlanStatus.STOP.getCode()) {
|
||||
// 未启用,获取方案的故障值与最新数据判断是否需要下发方案
|
||||
if(checkIsNeedIssuedPlan(protSettings, siteId)){
|
||||
if("3".equals(plan.getFaultLevel())){
|
||||
isHighLevel = true;//最高故障等级
|
||||
}
|
||||
// 延时
|
||||
final int faultDelay = plan.getFaultDelaySeconds().intValue();
|
||||
ScheduledFuture<?> delayTask = scheduledExecutorService.schedule(() -> {
|
||||
// 延时后再次确认是否仍满足触发条件(防止期间状态变化)
|
||||
if (checkIsNeedIssuedPlan(protSettings, siteId)) {
|
||||
// 判断是否需要生成告警
|
||||
if (isAlertAlarm == 1) {
|
||||
logger.info("<生成告警> 方案ID:{},站点:{}", plan.getId(), siteId);
|
||||
EmsAlarmRecords alarmRecords = addAlarmRecord(siteId,plan.getFaultName(),
|
||||
getAlarmLevel(plan.getFaultLevel()));
|
||||
emsAlarmRecordsMapper.insertEmsAlarmRecords(alarmRecords);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
// 是否有保护方案,有则通过modbus连接设备下发方案
|
||||
String protPlanJson = plan.getProtectionPlan();
|
||||
if (protPlanJson != null && !protPlanJson.isEmpty()) {
|
||||
logger.info("<下发保护方案> 方案内容:{}", protPlanJson);
|
||||
executeProtectionActions(protPlanJson,siteId,plan.getId(),plan.getFaultLevel()); // 执行Modbus指令
|
||||
}
|
||||
|
||||
// 更新方案状态为“已启用”
|
||||
logger.info("<方案已启用> 方案ID:{}", plan.getId());
|
||||
plan.setStatus(ProtPlanStatus.RUNNING.getCode());
|
||||
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
|
||||
if (Objects.equals(status, ProtPlanStatus.STOP.getCode())) {
|
||||
if (checkIsNeedIssuedPlan(faultSettings, siteId)) {
|
||||
int faultDelay = safeDelaySeconds(plan.getFaultDelaySeconds(), 0);
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
if (!checkIsNeedIssuedPlan(faultSettings, siteId)) {
|
||||
return;
|
||||
}
|
||||
}, faultDelay, TimeUnit.SECONDS);
|
||||
}
|
||||
} else {
|
||||
// 已启用,则获取方案的释放值与最新数据判断是否需要取消方案
|
||||
if(checkIsNeedCancelPlan(protSettings, siteId)){
|
||||
// 延时,
|
||||
int releaseDelay = plan.getReleaseDelaySeconds().intValue();
|
||||
ScheduledFuture<?> delayTask = scheduledExecutorService.schedule(() -> {
|
||||
// 判断是否已存在未处理告警,有着取消
|
||||
if(isAlertAlarm == 1){
|
||||
logger.info("<取消告警>");
|
||||
EmsAlarmRecords emsAlarmRecords = emsAlarmRecordsMapper.getFailedRecord(siteId,
|
||||
plan.getFaultName(),getAlarmLevel(plan.getFaultLevel()));
|
||||
if(emsAlarmRecords != null){
|
||||
emsAlarmRecords.setStatus(AlarmStatus.DONE.getCode());
|
||||
emsAlarmRecordsMapper.updateEmsAlarmRecords(emsAlarmRecords);
|
||||
}
|
||||
if (Integer.valueOf(1).equals(isAlertAlarm)) {
|
||||
EmsAlarmRecords alarmRecords = addAlarmRecord(siteId, plan.getFaultName(), getAlarmLevel(plan.getFaultLevel()));
|
||||
emsAlarmRecordsMapper.insertEmsAlarmRecords(alarmRecords);
|
||||
}
|
||||
// 更新方案状态为“未启用”
|
||||
logger.info("<方案变更为未启用> 方案ID:{}", plan.getId());
|
||||
plan.setStatus(ProtPlanStatus.STOP.getCode());
|
||||
plan.setStatus(ProtPlanStatus.RUNNING.getCode());
|
||||
plan.setUpdateBy("system");
|
||||
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
|
||||
// 更新该站点策略为启用
|
||||
updateStrategyRunning(siteId);
|
||||
}, releaseDelay, TimeUnit.SECONDS);
|
||||
refreshSiteProtectionConstraint(siteId);
|
||||
}, faultDelay, TimeUnit.SECONDS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return isHighLevel;
|
||||
}
|
||||
|
||||
// 下发保护方案
|
||||
private void executeProtectionActions(String protPlanJson, String siteId, Long planId, Integer faultLevel){
|
||||
final List<ProtectionPlanVo> protPlanList;
|
||||
try {
|
||||
protPlanList = objectMapper.readValue(
|
||||
protPlanJson,
|
||||
new TypeReference<List<ProtectionPlanVo>>() {}
|
||||
);
|
||||
if (protPlanList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历保护方案
|
||||
for (ProtectionPlanVo plan : protPlanList) {
|
||||
if (StringUtils.isEmpty(plan.getDeviceId()) || StringUtils.isEmpty(plan.getPoint())) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
// 给设备发送指令记录日志,并同步云端
|
||||
EmsFaultIssueLog faultIssueLog = createLogEntity(plan,siteId);
|
||||
faultIssueLog.setLogLevel(faultLevel);
|
||||
emsFaultIssueLogMapper.insertEmsFaultIssueLog(faultIssueLog);
|
||||
plan.setStatus(ProtPlanStatus.STOP.getCode());
|
||||
plan.setUpdateBy("system");
|
||||
emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
|
||||
refreshSiteProtectionConstraint(siteId);
|
||||
}, releaseDelay, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
// 通过modbus连接设备,发送数据
|
||||
executeSinglePlan(plan,siteId);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("下发保护方案失败,方案id为:", planId, e);
|
||||
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 EmsFaultIssueLog createLogEntity(ProtectionPlanVo plan,String siteId) {
|
||||
EmsFaultIssueLog faultIssueLog = new EmsFaultIssueLog();
|
||||
faultIssueLog.setLogId(UUID.randomUUID().toString());
|
||||
faultIssueLog.setLogTime(new Date());
|
||||
faultIssueLog.setSiteId(siteId);
|
||||
faultIssueLog.setDeviceId(plan.getDeviceId());
|
||||
faultIssueLog.setPoint(plan.getPoint());
|
||||
faultIssueLog.setValue(plan.getValue());
|
||||
faultIssueLog.setCreateBy("sys");
|
||||
faultIssueLog.setCreateTime(new Date());
|
||||
return faultIssueLog;
|
||||
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 void executeSinglePlan(ProtectionPlanVo plan, String siteId) throws Exception {
|
||||
String deviceId = plan.getDeviceId();
|
||||
// 获取设备地址信息
|
||||
EmsDevicesSetting device = emsDevicesSettingMapper.getDeviceBySiteAndDeviceId(deviceId, siteId);
|
||||
if (device == null || StringUtils.isEmpty(device.getIpAddress()) || device.getIpPort()==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;
|
||||
}
|
||||
// 获取设备连接
|
||||
ModbusConnectionWrapper wrapper = connectionManager.getConnection(device);
|
||||
if (wrapper == null || !wrapper.isActive()) {
|
||||
logger.info("<设备连接无效>");
|
||||
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());
|
||||
}
|
||||
|
||||
// 写入寄存器
|
||||
boolean success = modbusService.writeSingleRegister(
|
||||
wrapper.getConnection(),
|
||||
1,
|
||||
plan.getValue().intValue());
|
||||
|
||||
if (!success) {
|
||||
logger.error("写入失败,设备地址:{}", device.getIpAddress());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 校验释放值是否取消方案
|
||||
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) {
|
||||
BigDecimal releaseValue = BigDecimal.ZERO;
|
||||
|
||||
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();
|
||||
releaseValue = vo.getFaultValue();
|
||||
if(StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(point) || releaseValue == null
|
||||
|| StringUtils.isEmpty(vo.getReleaseOperator())){
|
||||
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){
|
||||
if (lastPointValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 拼接校验语句-最新值+比较方式+故障值+与下一点位关系(最后一个条件后不加关系)
|
||||
conditionSb.append(lastPointValue).append(vo.getReleaseOperator()).append(releaseValue);
|
||||
if (i < protSettings.size() - 1) {
|
||||
String relation = vo.getRelationNext();
|
||||
conditionSb.append(" ").append(relation).append(" ");
|
||||
conditionSb.append(" ").append(vo.getRelationNext()).append(" ");
|
||||
}
|
||||
|
||||
// 执行比较语句
|
||||
return executeWithParser(conditionSb.toString());
|
||||
}
|
||||
return true;
|
||||
return executeWithParser(conditionSb.toString());
|
||||
}
|
||||
|
||||
// 校验故障值是否需要下发方案
|
||||
private boolean checkIsNeedIssuedPlan(List<ProtectionSettingVo> protSettings, String siteId) {
|
||||
BigDecimal faultValue = BigDecimal.ZERO;
|
||||
|
||||
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();
|
||||
faultValue = vo.getFaultValue();
|
||||
if(StringUtils.isEmpty(deviceId) || StringUtils.isEmpty(point) || faultValue == null
|
||||
|| StringUtils.isEmpty(vo.getFaultOperator())){
|
||||
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){
|
||||
if (lastPointValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 拼接校验语句-最新值+比较方式+故障值+与下一点位关系(最后一个条件后不加关系)
|
||||
conditionSb.append(lastPointValue).append(vo.getFaultOperator()).append(faultValue);
|
||||
if (i < protSettings.size() - 1) {
|
||||
String relation = vo.getRelationNext();
|
||||
conditionSb.append(" ").append(relation).append(" ");
|
||||
conditionSb.append(" ").append(vo.getRelationNext()).append(" ");
|
||||
}
|
||||
|
||||
// 执行比较语句
|
||||
return executeWithParser(conditionSb.toString());
|
||||
}
|
||||
return true;
|
||||
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){
|
||||
if (mqttJson == null) {
|
||||
return null;
|
||||
}
|
||||
String jsonData = mqttJson.get("Data").toString();
|
||||
if(StringUtils.isEmpty(jsonData)){
|
||||
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 void updateStrategyRunning(String siteId) {
|
||||
// 获取是否有正在运行的策略,如果有则不更改
|
||||
EmsStrategyRunning emsStrategyRunning = emsStrategyRunningMapper.getRunningStrategy(siteId);
|
||||
if (emsStrategyRunning == null) {
|
||||
// 获取已存在并且状态为:未启用和已暂停的最晚一条策略,更新为已启用
|
||||
emsStrategyRunning = emsStrategyRunningMapper.getPendingStrategy(siteId);
|
||||
emsStrategyRunning.setStatus(StrategyStatus.RUNNING.getCode());
|
||||
emsStrategyRunningMapper.updateEmsStrategyRunning(emsStrategyRunning);
|
||||
}
|
||||
}
|
||||
|
||||
private EmsAlarmRecords addAlarmRecord(String siteId, String content,String level) {
|
||||
private EmsAlarmRecords addAlarmRecord(String siteId, String content, String level) {
|
||||
EmsAlarmRecords emsAlarmRecords = new EmsAlarmRecords();
|
||||
emsAlarmRecords.setSiteId(siteId);
|
||||
emsAlarmRecords.setAlarmContent(content);
|
||||
@ -351,29 +532,31 @@ public class ProtectionPlanTask {
|
||||
return emsAlarmRecords;
|
||||
}
|
||||
|
||||
// 故障等级-告警等级匹配
|
||||
private String getAlarmLevel(Integer faultLevel) {
|
||||
if (ObjectUtils.isEmpty(faultLevel) || faultLevel < 1 || faultLevel > 3) {
|
||||
logger.warn("非法故障等级:{},默认返回普通告警", faultLevel);
|
||||
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();
|
||||
case 1:
|
||||
return AlarmLevelStatus.GENERAL.getCode();
|
||||
case 2:
|
||||
return AlarmLevelStatus.SERIOUS.getCode();
|
||||
case 3:
|
||||
return AlarmLevelStatus.EMERGENCY.getCode();
|
||||
default:
|
||||
logger.error("未匹配的故障等级:{}", faultLevel);
|
||||
return AlarmLevelStatus.EMERGENCY.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义表达式解析器(仅支持简单运算符和逻辑关系)
|
||||
/**
|
||||
* 自定义表达式解析器(仅支持简单运算符和逻辑关系)
|
||||
*/
|
||||
public boolean executeWithParser(String conditionStr) {
|
||||
if (conditionStr == null || conditionStr.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. 拆分逻辑关系(提取 && 或 ||)
|
||||
List<String> logicRelations = new ArrayList<>();
|
||||
Pattern logicPattern = Pattern.compile("(&&|\\|\\|)");
|
||||
Matcher logicMatcher = logicPattern.matcher(conditionStr);
|
||||
@ -381,10 +564,7 @@ public class ProtectionPlanTask {
|
||||
logicRelations.add(logicMatcher.group());
|
||||
}
|
||||
|
||||
// 2. 拆分原子条件(如 "3.55>3.52")
|
||||
String[] atomicConditions = logicPattern.split(conditionStr);
|
||||
|
||||
// 3. 解析每个原子条件并计算结果
|
||||
List<Boolean> atomicResults = new ArrayList<>();
|
||||
Pattern conditionPattern = Pattern.compile("(\\d+\\.?\\d*)\\s*([><]=?|==)\\s*(\\d+\\.?\\d*)");
|
||||
for (String atomic : atomicConditions) {
|
||||
@ -393,11 +573,10 @@ public class ProtectionPlanTask {
|
||||
logger.error("无效的原子条件:{}", atomic);
|
||||
return false;
|
||||
}
|
||||
double left = Double.parseDouble(matcher.group(1)); // 左值(最新值)
|
||||
String operator = matcher.group(2); // 运算符
|
||||
double right = Double.parseDouble(matcher.group(3)); // 右值(故障值)
|
||||
double left = Double.parseDouble(matcher.group(1));
|
||||
String operator = matcher.group(2);
|
||||
double right = Double.parseDouble(matcher.group(3));
|
||||
|
||||
// 执行比较
|
||||
boolean result;
|
||||
switch (operator) {
|
||||
case ">":
|
||||
@ -422,7 +601,6 @@ public class ProtectionPlanTask {
|
||||
atomicResults.add(result);
|
||||
}
|
||||
|
||||
// 4. 组合原子结果(根据逻辑关系)
|
||||
boolean finalResult = atomicResults.get(0);
|
||||
for (int i = 0; i < logicRelations.size(); i++) {
|
||||
String relation = logicRelations.get(i);
|
||||
|
||||
@ -1,44 +1,103 @@
|
||||
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.EmsStrategyCurve;
|
||||
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.StrategyPowerDataVo;
|
||||
import com.xzzn.ems.domain.vo.ProtectionConstraintVo;
|
||||
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 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;
|
||||
|
||||
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("strategyPoller")
|
||||
public class StrategyPoller {
|
||||
private static final Logger logger = LoggerFactory.getLogger(StrategyPoller.class);
|
||||
|
||||
private final MqttLifecycleManager mqttLifecycleManager;
|
||||
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 ModbusConnectionManager connectionManager;
|
||||
@Autowired
|
||||
private ModbusService modbusService;
|
||||
@Autowired
|
||||
private EmsDevicesSettingMapper deviceRepo;
|
||||
@Autowired
|
||||
private EmsMqttMessageMapper emsMqttMessageMapper;
|
||||
@Autowired
|
||||
private EmsStrategyRunningMapper emsStrategyRunningMapper;
|
||||
@Autowired
|
||||
@ -46,33 +105,52 @@ public class StrategyPoller {
|
||||
@Autowired
|
||||
private EmsStrategyTimeConfigMapper emsStrategyTimeConfigMapper;
|
||||
@Autowired
|
||||
private EmsStrategyCurveMapper emsStrategyCurveMapper;
|
||||
|
||||
private EmsBatteryStackMapper emsBatteryStackMapper;
|
||||
@Autowired
|
||||
public StrategyPoller(MqttLifecycleManager mqttLifecycleManager) {
|
||||
this.mqttLifecycleManager = mqttLifecycleManager;
|
||||
}
|
||||
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("开始执行策略数据轮询...");
|
||||
logger.info("开始执行运行策略数据轮询...");
|
||||
List<StrategyRunningVo> strategyRunningVoList = emsStrategyRunningMapper.getPendingPollerStrategy(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);
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 处理获取到的数据,发到mqtt服务上
|
||||
// 处理获取到的运行策略数据,modbus发送指定的命令控制设备
|
||||
private void processData(StrategyRunningVo strategyVo) {
|
||||
logger.info("策略下发数据处理开始");
|
||||
logger.info("运行策略数据处理开始");
|
||||
// 根据运行策略获取主副策略的模板数据
|
||||
Long mainStrategyId = strategyVo.getMainStrategyId();
|
||||
Long auxStrategyId = strategyVo.getAuxStrategyId();
|
||||
@ -86,78 +164,613 @@ public class StrategyPoller {
|
||||
dealStrategyCurveData(auxStrategyId, siteId);
|
||||
}
|
||||
|
||||
// 策略数据下发-下方格式暂无
|
||||
logger.info("策略下发结束");
|
||||
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);
|
||||
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();
|
||||
}
|
||||
curve.setPowerData(powerDataVoList !=null ? JSON.toJSON(powerDataVoList).toString() : "");
|
||||
|
||||
// 记录推送记录
|
||||
emsStrategyCurveMapper.insertEmsStrategyCurve(curve);
|
||||
|
||||
// 设置已下发
|
||||
timeConfig.setIsPost(0);
|
||||
emsStrategyTimeConfigMapper.updateEmsStrategyTimeConfig(timeConfig);
|
||||
}
|
||||
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 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)
|
||||
};
|
||||
|
||||
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>
|
||||
</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;
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,15 @@
|
||||
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 com.xzzn.common.utils.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
import com.xzzn.common.utils.StringUtils;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 电池簇数据对象 ems_battery_cluster
|
||||
@ -28,8 +29,8 @@ public class EmsBatteryCluster extends BaseEntity
|
||||
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date dataUpdateTime;
|
||||
|
||||
/** 工作状态:0-正常 1-异常 2-停止 */
|
||||
@Excel(name = "工作状态:0-正常 1-异常 2-停止")
|
||||
/** 工作状态:0-运行 1-停机 2-故障 */
|
||||
@Excel(name = "工作状态:0-运行 1-停机 2-故障")
|
||||
private String workStatus;
|
||||
|
||||
/** 与PCS通信状态:0-正常 1-通信中断 2-异常 */
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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_day
|
||||
@ -75,6 +77,10 @@ public class EmsBatteryDataDay extends BaseEntity
|
||||
@Excel(name = "天级时间维度", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date dayTime;
|
||||
|
||||
/** 单体电池电流 */
|
||||
@Excel(name = "单体电池电流")
|
||||
private BigDecimal current;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
@ -215,6 +221,14 @@ public class EmsBatteryDataDay extends BaseEntity
|
||||
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)
|
||||
@ -237,6 +251,7 @@ public class EmsBatteryDataDay extends BaseEntity
|
||||
.append("clusterDeviceId", getClusterDeviceId())
|
||||
.append("interResistance", getInterResistance())
|
||||
.append("dayTime", getDayTime())
|
||||
.append("current", getCurrent())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +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_stack
|
||||
@ -27,8 +28,8 @@ public class EmsBatteryStack extends BaseEntity
|
||||
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date dataUpdateTime;
|
||||
|
||||
/** 工作状态:0-正常 1-异常 2-停止 */
|
||||
@Excel(name = "工作状态:0-正常 1-异常 2-停止")
|
||||
/** 工作状态:0-运行 1-停机 2-故障 */
|
||||
@Excel(name = "工作状态:0-运行 1-停机 2-故障")
|
||||
private String workStatus;
|
||||
|
||||
/** 与PCS通信状态:0-正常 1-通信中断 2-异常 */
|
||||
|
||||
@ -25,10 +25,6 @@ public class EmsDailyChargeData extends BaseEntity
|
||||
@Excel(name = "站点id")
|
||||
private String siteId;
|
||||
|
||||
/** 设备唯一标识符 */
|
||||
@Excel(name = "设备唯一标识符")
|
||||
private String deviceId;
|
||||
|
||||
/** 数据日期:yyyy-MM-dd */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "数据日期:yyyy-MM-dd", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@ -50,6 +46,14 @@ public class EmsDailyChargeData extends BaseEntity
|
||||
@Excel(name = "当日放电量")
|
||||
private BigDecimal dischargeData;
|
||||
|
||||
/** 总收入 */
|
||||
@Excel(name = "总收入")
|
||||
private BigDecimal totalRevenue;
|
||||
|
||||
/** 当日实时收入 */
|
||||
@Excel(name = "当日实时收入")
|
||||
private BigDecimal dayRevenue;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
@ -70,16 +74,6 @@ public class EmsDailyChargeData extends BaseEntity
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId)
|
||||
{
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceId()
|
||||
{
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDateTime(Date dateTime)
|
||||
{
|
||||
this.dateTime = dateTime;
|
||||
@ -130,17 +124,38 @@ public class EmsDailyChargeData extends BaseEntity
|
||||
return dischargeData;
|
||||
}
|
||||
|
||||
public void setTotalRevenue(BigDecimal totalRevenue)
|
||||
{
|
||||
this.totalRevenue = totalRevenue;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalRevenue()
|
||||
{
|
||||
return totalRevenue;
|
||||
}
|
||||
|
||||
public void setDayRevenue(BigDecimal dayRevenue)
|
||||
{
|
||||
this.dayRevenue = dayRevenue;
|
||||
}
|
||||
|
||||
public BigDecimal getDayRevenue()
|
||||
{
|
||||
return dayRevenue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("dateTime", getDateTime())
|
||||
.append("totalChargeData", getTotalChargeData())
|
||||
.append("totalDischargeData", getTotalDischargeData())
|
||||
.append("chargeData", getChargeData())
|
||||
.append("dischargeData", getDischargeData())
|
||||
.append("totalRevenue", getTotalRevenue())
|
||||
.append("dayRevenue", getDayRevenue())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
|
||||
@ -30,13 +30,9 @@ public class EmsDailyEnergyData extends BaseEntity
|
||||
@Excel(name = "数据日期:yyyy-MM-dd", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date dataDate;
|
||||
|
||||
/** 总收入 */
|
||||
@Excel(name = "总收入")
|
||||
private BigDecimal totalRevenue;
|
||||
|
||||
/** 当日实时收入 */
|
||||
@Excel(name = "当日实时收入")
|
||||
private BigDecimal dayRevenue;
|
||||
/** 数据小时(0-23) */
|
||||
@Excel(name = "数据小时(0-23)")
|
||||
private Integer dataHour;
|
||||
|
||||
/** 尖峰时段充电差值 */
|
||||
@Excel(name = "尖峰时段充电差值")
|
||||
@ -71,6 +67,7 @@ public class EmsDailyEnergyData extends BaseEntity
|
||||
private BigDecimal valleyDischargeDiff;
|
||||
|
||||
/** 差值计算时间(如2025-10-10 23:59:00) */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "差值计算时间", readConverterExp = "如=2025-10-10,2=3:59:00")
|
||||
private Date calcTime;
|
||||
|
||||
@ -104,24 +101,14 @@ public class EmsDailyEnergyData extends BaseEntity
|
||||
return dataDate;
|
||||
}
|
||||
|
||||
public void setTotalRevenue(BigDecimal totalRevenue)
|
||||
public void setDataHour(Integer dataHour)
|
||||
{
|
||||
this.totalRevenue = totalRevenue;
|
||||
this.dataHour = dataHour;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalRevenue()
|
||||
public Integer getDataHour()
|
||||
{
|
||||
return totalRevenue;
|
||||
}
|
||||
|
||||
public void setDayRevenue(BigDecimal dayRevenue)
|
||||
{
|
||||
this.dayRevenue = dayRevenue;
|
||||
}
|
||||
|
||||
public BigDecimal getDayRevenue()
|
||||
{
|
||||
return dayRevenue;
|
||||
return dataHour;
|
||||
}
|
||||
|
||||
public void setPeakChargeDiff(BigDecimal peakChargeDiff)
|
||||
@ -220,8 +207,7 @@ public class EmsDailyEnergyData extends BaseEntity
|
||||
.append("id", getId())
|
||||
.append("siteId", getSiteId())
|
||||
.append("dataDate", getDataDate())
|
||||
.append("totalRevenue", getTotalRevenue())
|
||||
.append("dayRevenue", getDayRevenue())
|
||||
.append("dataHour", getDataHour())
|
||||
.append("peakChargeDiff", getPeakChargeDiff())
|
||||
.append("peakDischargeDiff", getPeakDischargeDiff())
|
||||
.append("highChargeDiff", getHighChargeDiff())
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* Modbus设备配置对象 ems_devices_setting
|
||||
@ -102,10 +104,14 @@ public class EmsDevicesSetting extends BaseEntity
|
||||
@Excel(name = "设备类别,例如“STACK/CLUSTER/PCS等”")
|
||||
private String deviceCategory;
|
||||
|
||||
/** 设备运行状态:0-离线、1-待机、2-运行、3-故障、4-停机 */
|
||||
@Excel(name = "设备运行状态:0-离线、1-待机、2-运行、3-故障、4-停机")
|
||||
/** 设备运行状态:0-离线、1-在线 */
|
||||
@Excel(name = "设备运行状态:0-离线、1-在线")
|
||||
private String deviceStatus;
|
||||
|
||||
/** 设备运行状态:0-离线、1-在线 */
|
||||
@Excel(name = "工作状态:0-运行 1-停机 2-故障")
|
||||
private String workStatus;
|
||||
|
||||
/** 设备图像地址 */
|
||||
@Excel(name = "设备图像地址")
|
||||
private String pictureUrl;
|
||||
@ -328,6 +334,14 @@ public class EmsDevicesSetting extends BaseEntity
|
||||
this.deviceStatus = deviceStatus;
|
||||
}
|
||||
|
||||
public String getWorkStatus() {
|
||||
return workStatus;
|
||||
}
|
||||
|
||||
public void setWorkStatus(String workStatus) {
|
||||
this.workStatus = workStatus;
|
||||
}
|
||||
|
||||
public void setPictureUrl(String pictureUrl)
|
||||
{
|
||||
this.pictureUrl = pictureUrl;
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* PCS数据对象 ems_pcs_data
|
||||
@ -26,16 +28,16 @@ public class EmsPcsData extends BaseEntity
|
||||
@Excel(name = "数据更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date dataUpdateTime;
|
||||
|
||||
/** 工作状态:0-正常 1-异常 2-停止 */
|
||||
@Excel(name = "工作状态:0-正常 1-异常 2-停止")
|
||||
/** 工作状态:0-运行 1-停机 2-故障 */
|
||||
@Excel(name = "工作状态:0-运行 1-停机 2-故障")
|
||||
private String workStatus;
|
||||
|
||||
/** 并网状态:0-并网 1-未并网 */
|
||||
@Excel(name = "并网状态:0-并网 1-未并网")
|
||||
private String gridStatus;
|
||||
|
||||
/** 设备状态:0-在线 1-离线 2-维修中 */
|
||||
@Excel(name = "设备状态:0-在线 1-离线 2-维修中")
|
||||
/** 设备运行状态:0-离线、1-在线 */
|
||||
@Excel(name = "设备运行状态:0-离线、1-在线")
|
||||
private String deviceStatus;
|
||||
|
||||
/** 控制模式:0-远程 1-本地 */
|
||||
|
||||
192
ems-system/src/main/java/com/xzzn/ems/domain/EmsPcsSetting.java
Normal file
192
ems-system/src/main/java/com/xzzn/ems/domain/EmsPcsSetting.java
Normal file
@ -0,0 +1,192 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
/**
|
||||
* PCS设备配置对象 ems_pcs_setting
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-12-29
|
||||
*/
|
||||
public class EmsPcsSetting extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 设备ID,主键自增长 */
|
||||
private Long id;
|
||||
|
||||
/** 设备配置关联ID */
|
||||
@Excel(name = "设备配置关联ID")
|
||||
private Long deviceSettingId;
|
||||
|
||||
/** 开关机地址 */
|
||||
@Excel(name = "开关机地址")
|
||||
private String pointAddress;
|
||||
|
||||
/** 功率地址 */
|
||||
@Excel(name = "功率地址")
|
||||
private String powerAddress;
|
||||
|
||||
/** 开机指令 */
|
||||
@Excel(name = "开机指令")
|
||||
private String startCommand;
|
||||
|
||||
/** 关机指令 */
|
||||
@Excel(name = "关机指令")
|
||||
private String stopCommand;
|
||||
|
||||
/** 开机目标功率 */
|
||||
@Excel(name = "开机目标功率")
|
||||
private BigDecimal startPower;
|
||||
|
||||
/** 关机目标功率 */
|
||||
@Excel(name = "关机目标功率")
|
||||
private BigDecimal stopPower;
|
||||
|
||||
/** 目标功率倍率 */
|
||||
@Excel(name = "目标功率倍率")
|
||||
private BigDecimal powerMultiplier;
|
||||
|
||||
/** 电池簇数 */
|
||||
@Excel(name = "电池簇数")
|
||||
private Integer clusterNum;
|
||||
|
||||
/** 电池簇地址 */
|
||||
@Excel(name = "电池簇地址")
|
||||
private String clusterPointAddress;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setDeviceSettingId(Long deviceSettingId)
|
||||
{
|
||||
this.deviceSettingId = deviceSettingId;
|
||||
}
|
||||
|
||||
public Long getDeviceSettingId()
|
||||
{
|
||||
return deviceSettingId;
|
||||
}
|
||||
|
||||
public String getPowerAddress() {
|
||||
return powerAddress;
|
||||
}
|
||||
|
||||
public void setPowerAddress(String powerAddress) {
|
||||
this.powerAddress = powerAddress;
|
||||
}
|
||||
|
||||
public void setPointAddress(String pointAddress)
|
||||
{
|
||||
this.pointAddress = pointAddress;
|
||||
}
|
||||
|
||||
public String getPointAddress()
|
||||
{
|
||||
return pointAddress;
|
||||
}
|
||||
|
||||
public void setStartCommand(String startCommand)
|
||||
{
|
||||
this.startCommand = startCommand;
|
||||
}
|
||||
|
||||
public String getStartCommand()
|
||||
{
|
||||
return startCommand;
|
||||
}
|
||||
|
||||
public void setStopCommand(String stopCommand)
|
||||
{
|
||||
this.stopCommand = stopCommand;
|
||||
}
|
||||
|
||||
public String getStopCommand()
|
||||
{
|
||||
return stopCommand;
|
||||
}
|
||||
|
||||
public void setStartPower(BigDecimal startPower)
|
||||
{
|
||||
this.startPower = startPower;
|
||||
}
|
||||
|
||||
public BigDecimal getStartPower()
|
||||
{
|
||||
return startPower;
|
||||
}
|
||||
|
||||
public void setStopPower(BigDecimal stopPower)
|
||||
{
|
||||
this.stopPower = stopPower;
|
||||
}
|
||||
|
||||
public BigDecimal getStopPower()
|
||||
{
|
||||
return stopPower;
|
||||
}
|
||||
|
||||
public BigDecimal getPowerMultiplier()
|
||||
{
|
||||
return powerMultiplier;
|
||||
}
|
||||
|
||||
public void setPowerMultiplier(BigDecimal powerMultiplier)
|
||||
{
|
||||
this.powerMultiplier = powerMultiplier;
|
||||
}
|
||||
|
||||
public void setClusterNum(Integer clusterNum)
|
||||
{
|
||||
this.clusterNum = clusterNum;
|
||||
}
|
||||
|
||||
public Integer getClusterNum()
|
||||
{
|
||||
return clusterNum;
|
||||
}
|
||||
|
||||
public void setClusterPointAddress(String clusterPointAddress)
|
||||
{
|
||||
this.clusterPointAddress = clusterPointAddress;
|
||||
}
|
||||
|
||||
public String getClusterPointAddress()
|
||||
{
|
||||
return clusterPointAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("deviceSettingId", getDeviceSettingId())
|
||||
.append("pointAddress", getPointAddress())
|
||||
.append("startCommand", getStartCommand())
|
||||
.append("stopCommand", getStopCommand())
|
||||
.append("startPower", getStartPower())
|
||||
.append("stopPower", getStopPower())
|
||||
.append("powerMultiplier", getPowerMultiplier())
|
||||
.append("clusterNum", getClusterNum())
|
||||
.append("clusterPointAddress", getClusterPointAddress())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
public class EmsPointCalcConfig extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
|
||||
@Excel(name = "点位ID")
|
||||
private String pointId;
|
||||
|
||||
@Excel(name = "站点ID")
|
||||
private String siteId;
|
||||
|
||||
@Excel(name = "设备类型")
|
||||
private String deviceCategory;
|
||||
|
||||
@Excel(name = "点位名称")
|
||||
private String pointName;
|
||||
|
||||
@Excel(name = "数据键")
|
||||
private String dataKey;
|
||||
|
||||
@Excel(name = "点位描述")
|
||||
private String pointDesc;
|
||||
|
||||
@Excel(name = "单位")
|
||||
private String dataUnit;
|
||||
|
||||
@Excel(name = "计算表达式")
|
||||
private String calcExpression;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPointId() {
|
||||
return pointId;
|
||||
}
|
||||
|
||||
public void setPointId(String pointId) {
|
||||
this.pointId = pointId;
|
||||
}
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getPointName() {
|
||||
return pointName;
|
||||
}
|
||||
|
||||
public void setPointName(String pointName) {
|
||||
this.pointName = pointName;
|
||||
}
|
||||
|
||||
public String getDeviceCategory() {
|
||||
return deviceCategory;
|
||||
}
|
||||
|
||||
public void setDeviceCategory(String deviceCategory) {
|
||||
this.deviceCategory = deviceCategory;
|
||||
}
|
||||
|
||||
public String getDataKey() {
|
||||
return dataKey;
|
||||
}
|
||||
|
||||
public void setDataKey(String dataKey) {
|
||||
this.dataKey = dataKey;
|
||||
}
|
||||
|
||||
public String getPointDesc() {
|
||||
return pointDesc;
|
||||
}
|
||||
|
||||
public void setPointDesc(String pointDesc) {
|
||||
this.pointDesc = pointDesc;
|
||||
}
|
||||
|
||||
public String getDataUnit() {
|
||||
return dataUnit;
|
||||
}
|
||||
|
||||
public void setDataUnit(String dataUnit) {
|
||||
this.dataUnit = dataUnit;
|
||||
}
|
||||
|
||||
public String getCalcExpression() {
|
||||
return calcExpression;
|
||||
}
|
||||
|
||||
public void setCalcExpression(String calcExpression) {
|
||||
this.calcExpression = calcExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("pointId", getPointId())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceCategory", getDeviceCategory())
|
||||
.append("pointName", getPointName())
|
||||
.append("dataKey", getDataKey())
|
||||
.append("pointDesc", getPointDesc())
|
||||
.append("dataUnit", getDataUnit())
|
||||
.append("calcExpression", getCalcExpression())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
313
ems-system/src/main/java/com/xzzn/ems/domain/EmsPointConfig.java
Normal file
313
ems-system/src/main/java/com/xzzn/ems/domain/EmsPointConfig.java
Normal file
@ -0,0 +1,313 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 点位配置对象 ems_point_config
|
||||
*/
|
||||
public class EmsPointConfig extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
|
||||
@Excel(name = "点位ID")
|
||||
private String pointId;
|
||||
|
||||
@Excel(name = "站点ID")
|
||||
private String siteId;
|
||||
|
||||
@Excel(name = "设备类型")
|
||||
private String deviceCategory;
|
||||
|
||||
@Excel(name = "设备ID")
|
||||
private String deviceId;
|
||||
|
||||
@Excel(name = "点位名称")
|
||||
private String pointName;
|
||||
|
||||
@Excel(name = "数据键")
|
||||
private String dataKey;
|
||||
|
||||
@Excel(name = "点位描述")
|
||||
private String pointDesc;
|
||||
|
||||
@Excel(name = "寄存器地址")
|
||||
private String registerAddress;
|
||||
|
||||
@Excel(name = "单位")
|
||||
private String dataUnit;
|
||||
|
||||
@Excel(name = "A系数")
|
||||
private BigDecimal dataA;
|
||||
|
||||
@Excel(name = "K系数")
|
||||
private BigDecimal dataK;
|
||||
|
||||
@Excel(name = "B系数")
|
||||
private BigDecimal dataB;
|
||||
|
||||
@Excel(name = "位偏移")
|
||||
private Integer dataBit;
|
||||
|
||||
@Excel(name = "是否报警点位", readConverterExp = "0=否,1=是")
|
||||
private Integer isAlarm;
|
||||
|
||||
@Excel(name = "点位类型")
|
||||
private String pointType;
|
||||
|
||||
@Excel(name = "计算表达式")
|
||||
private String calcExpression;
|
||||
|
||||
@Excel(name = "是否启用采集", readConverterExp = "0=否,1=是")
|
||||
private Integer collectEnabled;
|
||||
|
||||
@Excel(name = "采集来源")
|
||||
private String collectSource;
|
||||
|
||||
@Excel(name = "Modbus寄存器类型")
|
||||
private String modbusRegisterType;
|
||||
|
||||
@Excel(name = "Modbus数据类型")
|
||||
private String modbusDataType;
|
||||
|
||||
@Excel(name = "Modbus读取顺序")
|
||||
private Integer modbusReadOrder;
|
||||
|
||||
@Excel(name = "Modbus分组")
|
||||
private String modbusGroup;
|
||||
|
||||
@Excel(name = "小数位数")
|
||||
private Integer decimalScale;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPointId() {
|
||||
return pointId;
|
||||
}
|
||||
|
||||
public void setPointId(String pointId) {
|
||||
this.pointId = pointId;
|
||||
}
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getDeviceCategory() {
|
||||
return deviceCategory;
|
||||
}
|
||||
|
||||
public void setDeviceCategory(String deviceCategory) {
|
||||
this.deviceCategory = deviceCategory;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getPointName() {
|
||||
return pointName;
|
||||
}
|
||||
|
||||
public void setPointName(String pointName) {
|
||||
this.pointName = pointName;
|
||||
}
|
||||
|
||||
public String getDataKey() {
|
||||
return dataKey;
|
||||
}
|
||||
|
||||
public void setDataKey(String dataKey) {
|
||||
this.dataKey = dataKey;
|
||||
}
|
||||
|
||||
public String getPointDesc() {
|
||||
return pointDesc;
|
||||
}
|
||||
|
||||
public void setPointDesc(String pointDesc) {
|
||||
this.pointDesc = pointDesc;
|
||||
}
|
||||
|
||||
public String getRegisterAddress() {
|
||||
return registerAddress;
|
||||
}
|
||||
|
||||
public void setRegisterAddress(String registerAddress) {
|
||||
this.registerAddress = registerAddress;
|
||||
}
|
||||
|
||||
public String getDataUnit() {
|
||||
return dataUnit;
|
||||
}
|
||||
|
||||
public void setDataUnit(String dataUnit) {
|
||||
this.dataUnit = dataUnit;
|
||||
}
|
||||
|
||||
public BigDecimal getDataA() {
|
||||
return dataA;
|
||||
}
|
||||
|
||||
public void setDataA(BigDecimal dataA) {
|
||||
this.dataA = dataA;
|
||||
}
|
||||
|
||||
public BigDecimal getDataK() {
|
||||
return dataK;
|
||||
}
|
||||
|
||||
public void setDataK(BigDecimal dataK) {
|
||||
this.dataK = dataK;
|
||||
}
|
||||
|
||||
public BigDecimal getDataB() {
|
||||
return dataB;
|
||||
}
|
||||
|
||||
public void setDataB(BigDecimal dataB) {
|
||||
this.dataB = dataB;
|
||||
}
|
||||
|
||||
public Integer getDataBit() {
|
||||
return dataBit;
|
||||
}
|
||||
|
||||
public void setDataBit(Integer dataBit) {
|
||||
this.dataBit = dataBit;
|
||||
}
|
||||
|
||||
public Integer getIsAlarm() {
|
||||
return isAlarm;
|
||||
}
|
||||
|
||||
public void setIsAlarm(Integer isAlarm) {
|
||||
this.isAlarm = isAlarm;
|
||||
}
|
||||
|
||||
public String getPointType() {
|
||||
return pointType;
|
||||
}
|
||||
|
||||
public void setPointType(String pointType) {
|
||||
this.pointType = pointType;
|
||||
}
|
||||
|
||||
public String getCalcExpression() {
|
||||
return calcExpression;
|
||||
}
|
||||
|
||||
public void setCalcExpression(String calcExpression) {
|
||||
this.calcExpression = calcExpression;
|
||||
}
|
||||
|
||||
public Integer getCollectEnabled() {
|
||||
return collectEnabled;
|
||||
}
|
||||
|
||||
public void setCollectEnabled(Integer collectEnabled) {
|
||||
this.collectEnabled = collectEnabled;
|
||||
}
|
||||
|
||||
public String getCollectSource() {
|
||||
return collectSource;
|
||||
}
|
||||
|
||||
public void setCollectSource(String collectSource) {
|
||||
this.collectSource = collectSource;
|
||||
}
|
||||
|
||||
public String getModbusRegisterType() {
|
||||
return modbusRegisterType;
|
||||
}
|
||||
|
||||
public void setModbusRegisterType(String modbusRegisterType) {
|
||||
this.modbusRegisterType = modbusRegisterType;
|
||||
}
|
||||
|
||||
public String getModbusDataType() {
|
||||
return modbusDataType;
|
||||
}
|
||||
|
||||
public void setModbusDataType(String modbusDataType) {
|
||||
this.modbusDataType = modbusDataType;
|
||||
}
|
||||
|
||||
public Integer getModbusReadOrder() {
|
||||
return modbusReadOrder;
|
||||
}
|
||||
|
||||
public void setModbusReadOrder(Integer modbusReadOrder) {
|
||||
this.modbusReadOrder = modbusReadOrder;
|
||||
}
|
||||
|
||||
public String getModbusGroup() {
|
||||
return modbusGroup;
|
||||
}
|
||||
|
||||
public void setModbusGroup(String modbusGroup) {
|
||||
this.modbusGroup = modbusGroup;
|
||||
}
|
||||
|
||||
public Integer getDecimalScale() {
|
||||
return decimalScale;
|
||||
}
|
||||
|
||||
public void setDecimalScale(Integer decimalScale) {
|
||||
this.decimalScale = decimalScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("pointId", getPointId())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceCategory", getDeviceCategory())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("pointName", getPointName())
|
||||
.append("dataKey", getDataKey())
|
||||
.append("pointDesc", getPointDesc())
|
||||
.append("registerAddress", getRegisterAddress())
|
||||
.append("dataUnit", getDataUnit())
|
||||
.append("dataA", getDataA())
|
||||
.append("dataK", getDataK())
|
||||
.append("dataB", getDataB())
|
||||
.append("dataBit", getDataBit())
|
||||
.append("isAlarm", getIsAlarm())
|
||||
.append("pointType", getPointType())
|
||||
.append("calcExpression", getCalcExpression())
|
||||
.append("collectEnabled", getCollectEnabled())
|
||||
.append("collectSource", getCollectSource())
|
||||
.append("modbusRegisterType", getModbusRegisterType())
|
||||
.append("modbusDataType", getModbusDataType())
|
||||
.append("modbusReadOrder", getModbusReadOrder())
|
||||
.append("modbusGroup", getModbusGroup())
|
||||
.append("decimalScale", getDecimalScale())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,12 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 点位匹配对象 ems_point_match
|
||||
@ -78,6 +81,20 @@ public class EmsPointMatch extends BaseEntity
|
||||
@Excel(name = "设备唯一标识符")
|
||||
private String deviceId;
|
||||
|
||||
/** 设备点位数据转换公式:二次函数(f(x) = Ax² + Kx + B) */
|
||||
/** 二次项系数 */
|
||||
@Excel(name = "二次项系数")
|
||||
private BigDecimal a;
|
||||
|
||||
/** 一次项系数 */
|
||||
@Excel(name = "一次项系数")
|
||||
private BigDecimal k;
|
||||
|
||||
/** 常量 */
|
||||
@Excel(name = "常量")
|
||||
private BigDecimal b;
|
||||
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
@ -232,6 +249,35 @@ public class EmsPointMatch extends BaseEntity
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public BigDecimal getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(BigDecimal a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public BigDecimal getK() {
|
||||
return k;
|
||||
}
|
||||
|
||||
public void setK(BigDecimal k) {
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
public BigDecimal getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setB(BigDecimal b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
/** 二次函数公式转换点位数据: A * value² + K * value + B */
|
||||
public int convert(BigDecimal value) {
|
||||
return a.multiply(value.pow(2)).add(k.multiply(value)).add(b).intValueExact();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
@ -256,6 +302,9 @@ public class EmsPointMatch extends BaseEntity
|
||||
.append("remark", getRemark())
|
||||
.append("isAlarm", getIsAlarm())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("a", getA())
|
||||
.append("k", getK())
|
||||
.append("b", getB())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 单站监控展示数据对象(按分组落到 ems_site_monitor_data_home/sbjk/tjbb)
|
||||
*/
|
||||
public class EmsSiteMonitorData extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
private String siteId;
|
||||
private String fieldCode;
|
||||
private String fieldValue;
|
||||
private Date valueTime;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getFieldCode() {
|
||||
return fieldCode;
|
||||
}
|
||||
|
||||
public void setFieldCode(String fieldCode) {
|
||||
this.fieldCode = fieldCode;
|
||||
}
|
||||
|
||||
public String getFieldValue() {
|
||||
return fieldValue;
|
||||
}
|
||||
|
||||
public void setFieldValue(String fieldValue) {
|
||||
this.fieldValue = fieldValue;
|
||||
}
|
||||
|
||||
public Date getValueTime() {
|
||||
return valueTime;
|
||||
}
|
||||
|
||||
public void setValueTime(Date valueTime) {
|
||||
this.valueTime = valueTime;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 单站监控配置项定义对象 ems_site_monitor_item
|
||||
*/
|
||||
public class EmsSiteMonitorItem extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
private String moduleCode;
|
||||
private String moduleName;
|
||||
private String menuCode;
|
||||
private String menuName;
|
||||
private String sectionName;
|
||||
private String fieldCode;
|
||||
private String fieldName;
|
||||
private Integer sortNo;
|
||||
private Integer status;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getModuleCode() {
|
||||
return moduleCode;
|
||||
}
|
||||
|
||||
public void setModuleCode(String moduleCode) {
|
||||
this.moduleCode = moduleCode;
|
||||
}
|
||||
|
||||
public String getModuleName() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
public void setModuleName(String moduleName) {
|
||||
this.moduleName = moduleName;
|
||||
}
|
||||
|
||||
public String getMenuCode() {
|
||||
return menuCode;
|
||||
}
|
||||
|
||||
public void setMenuCode(String menuCode) {
|
||||
this.menuCode = menuCode;
|
||||
}
|
||||
|
||||
public String getMenuName() {
|
||||
return menuName;
|
||||
}
|
||||
|
||||
public void setMenuName(String menuName) {
|
||||
this.menuName = menuName;
|
||||
}
|
||||
|
||||
public String getSectionName() {
|
||||
return sectionName;
|
||||
}
|
||||
|
||||
public void setSectionName(String sectionName) {
|
||||
this.sectionName = sectionName;
|
||||
}
|
||||
|
||||
public String getFieldCode() {
|
||||
return fieldCode;
|
||||
}
|
||||
|
||||
public void setFieldCode(String fieldCode) {
|
||||
this.fieldCode = fieldCode;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public void setFieldName(String fieldName) {
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
public Integer getSortNo() {
|
||||
return sortNo;
|
||||
}
|
||||
|
||||
public void setSortNo(Integer sortNo) {
|
||||
this.sortNo = sortNo;
|
||||
}
|
||||
|
||||
public Integer getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Integer status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 单站监控字段点位映射对象 ems_site_monitor_point_match
|
||||
*/
|
||||
public class EmsSiteMonitorPointMatch extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
private String siteId;
|
||||
private String fieldCode;
|
||||
private String deviceId;
|
||||
private String dataPoint;
|
||||
private String fixedDataPoint;
|
||||
private Integer useFixedDisplay;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getFieldCode() {
|
||||
return fieldCode;
|
||||
}
|
||||
|
||||
public void setFieldCode(String fieldCode) {
|
||||
this.fieldCode = fieldCode;
|
||||
}
|
||||
|
||||
public String getDataPoint() {
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
public void setDataPoint(String dataPoint) {
|
||||
this.dataPoint = dataPoint;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getFixedDataPoint() {
|
||||
return fixedDataPoint;
|
||||
}
|
||||
|
||||
public void setFixedDataPoint(String fixedDataPoint) {
|
||||
this.fixedDataPoint = fixedDataPoint;
|
||||
}
|
||||
|
||||
public Integer getUseFixedDisplay() {
|
||||
return useFixedDisplay;
|
||||
}
|
||||
|
||||
public void setUseFixedDisplay(Integer useFixedDisplay) {
|
||||
this.useFixedDisplay = useFixedDisplay;
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,10 @@ public class EmsSiteSetting extends BaseEntity
|
||||
@Excel(name = "站点名称")
|
||||
private String siteName;
|
||||
|
||||
/** 站点简称 */
|
||||
@Excel(name = "站点简称")
|
||||
private String siteShortName;
|
||||
|
||||
/** 站点地址 */
|
||||
@Excel(name = "站点地址")
|
||||
private String siteAddress;
|
||||
@ -54,6 +58,9 @@ public class EmsSiteSetting extends BaseEntity
|
||||
@Excel(name = "站点id")
|
||||
private String siteId;
|
||||
|
||||
/** 授权状态:true-已授权,false-未授权 */
|
||||
private Boolean authorized;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
@ -74,6 +81,16 @@ public class EmsSiteSetting extends BaseEntity
|
||||
return siteName;
|
||||
}
|
||||
|
||||
public void setSiteShortName(String siteShortName)
|
||||
{
|
||||
this.siteShortName = siteShortName;
|
||||
}
|
||||
|
||||
public String getSiteShortName()
|
||||
{
|
||||
return siteShortName;
|
||||
}
|
||||
|
||||
public void setSiteAddress(String siteAddress)
|
||||
{
|
||||
this.siteAddress = siteAddress;
|
||||
@ -144,11 +161,22 @@ public class EmsSiteSetting extends BaseEntity
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setAuthorized(Boolean authorized)
|
||||
{
|
||||
this.authorized = authorized;
|
||||
}
|
||||
|
||||
public Boolean getAuthorized()
|
||||
{
|
||||
return authorized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("siteName", getSiteName())
|
||||
.append("siteShortName", getSiteShortName())
|
||||
.append("siteAddress", getSiteAddress())
|
||||
.append("runningTime", getRunningTime())
|
||||
.append("latitude", getLatitude())
|
||||
@ -161,6 +189,7 @@ public class EmsSiteSetting extends BaseEntity
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("siteId", getSiteId())
|
||||
.append("authorized", getAuthorized())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
210
ems-system/src/main/java/com/xzzn/ems/domain/EmsStrategyLog.java
Normal file
210
ems-system/src/main/java/com/xzzn/ems/domain/EmsStrategyLog.java
Normal file
@ -0,0 +1,210 @@
|
||||
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_strategy_log
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2025-12-26
|
||||
*/
|
||||
public class EmsStrategyLog extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** $column.columnComment */
|
||||
private Long id;
|
||||
|
||||
/** 策略ID */
|
||||
@Excel(name = "策略ID")
|
||||
private Long strategyId;
|
||||
|
||||
/** 模版id */
|
||||
@Excel(name = "模版id")
|
||||
private String templateId;
|
||||
|
||||
/** 站点id */
|
||||
@Excel(name = "站点id")
|
||||
private String siteId;
|
||||
|
||||
/** 设备唯一标识符 */
|
||||
@Excel(name = "设备唯一标识符")
|
||||
private String deviceId;
|
||||
|
||||
/** 开始时间 */
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@Excel(name = "开始时间", width = 30, dateFormat = "HH:mm")
|
||||
private Date startTime;
|
||||
|
||||
/** 结束时间 */
|
||||
@JsonFormat(pattern = "HH:mm")
|
||||
@Excel(name = "结束时间", width = 30, dateFormat = "HH:mm")
|
||||
private Date endTime;
|
||||
|
||||
/** 功率 (kW) */
|
||||
@Excel(name = "功率 (kW)")
|
||||
private BigDecimal chargeDischargePower;
|
||||
|
||||
/** 充电状态,如“1-充电”、“2-待机”、“3-放电” */
|
||||
@Excel(name = "充电状态,如“1-充电”、“2-待机”、“3-放电”")
|
||||
private String chargeStatus;
|
||||
|
||||
/** 执行时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@Excel(name = "执行时间", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
private Date executionDate;
|
||||
|
||||
/** 防逆流, 1-是、0-否 */
|
||||
@Excel(name = "防逆流, 1-是、0-否")
|
||||
private Integer antiReverse;
|
||||
|
||||
/** PCS降功率类型 */
|
||||
@Excel(name = "PCS降功率类型,0-降低功率、1-增加功率")
|
||||
private Integer powerDownType;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setStrategyId(Long strategyId)
|
||||
{
|
||||
this.strategyId = strategyId;
|
||||
}
|
||||
|
||||
public Long getStrategyId()
|
||||
{
|
||||
return strategyId;
|
||||
}
|
||||
|
||||
public void setTemplateId(String templateId)
|
||||
{
|
||||
this.templateId = templateId;
|
||||
}
|
||||
|
||||
public String getTemplateId()
|
||||
{
|
||||
return templateId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId)
|
||||
{
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getSiteId()
|
||||
{
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public void setStartTime(Date startTime)
|
||||
{
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public Date getStartTime()
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setEndTime(Date endTime)
|
||||
{
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public Date getEndTime()
|
||||
{
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setChargeDischargePower(BigDecimal chargeDischargePower)
|
||||
{
|
||||
this.chargeDischargePower = chargeDischargePower;
|
||||
}
|
||||
|
||||
public BigDecimal getChargeDischargePower()
|
||||
{
|
||||
return chargeDischargePower;
|
||||
}
|
||||
|
||||
public void setChargeStatus(String chargeStatus)
|
||||
{
|
||||
this.chargeStatus = chargeStatus;
|
||||
}
|
||||
|
||||
public String getChargeStatus()
|
||||
{
|
||||
return chargeStatus;
|
||||
}
|
||||
|
||||
public void setExecutionDate(Date executionDate)
|
||||
{
|
||||
this.executionDate = executionDate;
|
||||
}
|
||||
|
||||
public Date getExecutionDate()
|
||||
{
|
||||
return executionDate;
|
||||
}
|
||||
|
||||
public Integer getAntiReverse() {
|
||||
return antiReverse;
|
||||
}
|
||||
|
||||
public void setAntiReverse(Integer antiReverse) {
|
||||
this.antiReverse = antiReverse;
|
||||
}
|
||||
|
||||
public Integer getPowerDownType() {
|
||||
return powerDownType;
|
||||
}
|
||||
|
||||
public void setPowerDownType(Integer powerDownType) {
|
||||
this.powerDownType = powerDownType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("strategyId", getStrategyId())
|
||||
.append("templateId", getTemplateId())
|
||||
.append("siteId", getSiteId())
|
||||
.append("deviceId", getDeviceId())
|
||||
.append("antiReverse", getAntiReverse())
|
||||
.append("startTime", getStartTime())
|
||||
.append("endTime", getEndTime())
|
||||
.append("chargeDischargePower", getChargeDischargePower())
|
||||
.append("chargeStatus", getChargeStatus())
|
||||
.append("executionDate", getExecutionDate())
|
||||
.append("antiReverse", getAntiReverse())
|
||||
.append("powerDownType", getPowerDownType())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,221 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 策略运行参数配置对象 ems_strategy_runtime_config
|
||||
*
|
||||
* @author xzzn
|
||||
* @date 2026-02-13
|
||||
*/
|
||||
public class EmsStrategyRuntimeConfig extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键 */
|
||||
private Long id;
|
||||
|
||||
/** 站点ID */
|
||||
@Excel(name = "站点ID")
|
||||
private String siteId;
|
||||
|
||||
/** SOC下限(%) */
|
||||
@Excel(name = "SOC下限(%)")
|
||||
private BigDecimal socDown;
|
||||
|
||||
/** SOC上限(%) */
|
||||
@Excel(name = "SOC上限(%)")
|
||||
private BigDecimal socUp;
|
||||
|
||||
/** 防逆流阈值(kW) */
|
||||
@Excel(name = "防逆流阈值(kW)")
|
||||
private BigDecimal antiReverseThreshold;
|
||||
|
||||
/** 防逆流阈值上浮比例(%) */
|
||||
@Excel(name = "防逆流阈值上浮比例(%)")
|
||||
private BigDecimal antiReverseRangePercent;
|
||||
|
||||
/** 防逆流恢复上限(kW) */
|
||||
@Excel(name = "防逆流恢复上限(kW)")
|
||||
private BigDecimal antiReverseUp;
|
||||
|
||||
/** 防逆流降功率比例(%) */
|
||||
@Excel(name = "防逆流降功率比例(%)")
|
||||
private BigDecimal antiReversePowerDownPercent;
|
||||
/** 防逆流硬停阈值(kW) */
|
||||
@Excel(name = "防逆流硬停阈值(kW)")
|
||||
private BigDecimal antiReverseHardStopThreshold;
|
||||
/** 设定功率倍率 */
|
||||
@Excel(name = "设定功率倍率")
|
||||
private BigDecimal powerSetMultiplier;
|
||||
|
||||
/** 保护介入开关(1-启用,0-禁用) */
|
||||
@Excel(name = "保护介入开关")
|
||||
private Integer protectInterveneEnable;
|
||||
|
||||
/** 一级保护降额比例(%) */
|
||||
@Excel(name = "一级保护降额比例(%)")
|
||||
private BigDecimal protectL1DeratePercent;
|
||||
|
||||
/** 保护释放稳定时长(秒) */
|
||||
@Excel(name = "保护释放稳定时长(秒)")
|
||||
private Integer protectRecoveryStableSeconds;
|
||||
|
||||
/** 三级保护锁存开关(1-启用,0-禁用) */
|
||||
@Excel(name = "三级保护锁存开关")
|
||||
private Integer protectL3LatchEnable;
|
||||
|
||||
/** 保护冲突策略 */
|
||||
@Excel(name = "保护冲突策略")
|
||||
private String protectConflictPolicy;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public BigDecimal getSocDown() {
|
||||
return socDown;
|
||||
}
|
||||
|
||||
public void setSocDown(BigDecimal socDown) {
|
||||
this.socDown = socDown;
|
||||
}
|
||||
|
||||
public BigDecimal getSocUp() {
|
||||
return socUp;
|
||||
}
|
||||
|
||||
public void setSocUp(BigDecimal socUp) {
|
||||
this.socUp = socUp;
|
||||
}
|
||||
|
||||
public BigDecimal getAntiReverseThreshold() {
|
||||
return antiReverseThreshold;
|
||||
}
|
||||
|
||||
public void setAntiReverseThreshold(BigDecimal antiReverseThreshold) {
|
||||
this.antiReverseThreshold = antiReverseThreshold;
|
||||
}
|
||||
|
||||
public BigDecimal getAntiReverseRangePercent() {
|
||||
return antiReverseRangePercent;
|
||||
}
|
||||
|
||||
public void setAntiReverseRangePercent(BigDecimal antiReverseRangePercent) {
|
||||
this.antiReverseRangePercent = antiReverseRangePercent;
|
||||
}
|
||||
|
||||
public BigDecimal getAntiReverseUp() {
|
||||
return antiReverseUp;
|
||||
}
|
||||
|
||||
public void setAntiReverseUp(BigDecimal antiReverseUp) {
|
||||
this.antiReverseUp = antiReverseUp;
|
||||
}
|
||||
|
||||
public BigDecimal getAntiReversePowerDownPercent() {
|
||||
return antiReversePowerDownPercent;
|
||||
}
|
||||
|
||||
public void setAntiReversePowerDownPercent(BigDecimal antiReversePowerDownPercent) {
|
||||
this.antiReversePowerDownPercent = antiReversePowerDownPercent;
|
||||
}
|
||||
|
||||
public BigDecimal getAntiReverseHardStopThreshold() {
|
||||
return antiReverseHardStopThreshold;
|
||||
}
|
||||
|
||||
public void setAntiReverseHardStopThreshold(BigDecimal antiReverseHardStopThreshold) {
|
||||
this.antiReverseHardStopThreshold = antiReverseHardStopThreshold;
|
||||
}
|
||||
|
||||
public BigDecimal getPowerSetMultiplier() {
|
||||
return powerSetMultiplier;
|
||||
}
|
||||
|
||||
public void setPowerSetMultiplier(BigDecimal powerSetMultiplier) {
|
||||
this.powerSetMultiplier = powerSetMultiplier;
|
||||
}
|
||||
|
||||
public Integer getProtectInterveneEnable() {
|
||||
return protectInterveneEnable;
|
||||
}
|
||||
|
||||
public void setProtectInterveneEnable(Integer protectInterveneEnable) {
|
||||
this.protectInterveneEnable = protectInterveneEnable;
|
||||
}
|
||||
|
||||
public BigDecimal getProtectL1DeratePercent() {
|
||||
return protectL1DeratePercent;
|
||||
}
|
||||
|
||||
public void setProtectL1DeratePercent(BigDecimal protectL1DeratePercent) {
|
||||
this.protectL1DeratePercent = protectL1DeratePercent;
|
||||
}
|
||||
|
||||
public Integer getProtectRecoveryStableSeconds() {
|
||||
return protectRecoveryStableSeconds;
|
||||
}
|
||||
|
||||
public void setProtectRecoveryStableSeconds(Integer protectRecoveryStableSeconds) {
|
||||
this.protectRecoveryStableSeconds = protectRecoveryStableSeconds;
|
||||
}
|
||||
|
||||
public Integer getProtectL3LatchEnable() {
|
||||
return protectL3LatchEnable;
|
||||
}
|
||||
|
||||
public void setProtectL3LatchEnable(Integer protectL3LatchEnable) {
|
||||
this.protectL3LatchEnable = protectL3LatchEnable;
|
||||
}
|
||||
|
||||
public String getProtectConflictPolicy() {
|
||||
return protectConflictPolicy;
|
||||
}
|
||||
|
||||
public void setProtectConflictPolicy(String protectConflictPolicy) {
|
||||
this.protectConflictPolicy = protectConflictPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("id", getId())
|
||||
.append("siteId", getSiteId())
|
||||
.append("socDown", getSocDown())
|
||||
.append("socUp", getSocUp())
|
||||
.append("antiReverseThreshold", getAntiReverseThreshold())
|
||||
.append("antiReverseRangePercent", getAntiReverseRangePercent())
|
||||
.append("antiReverseUp", getAntiReverseUp())
|
||||
.append("antiReversePowerDownPercent", getAntiReversePowerDownPercent())
|
||||
.append("antiReverseHardStopThreshold", getAntiReverseHardStopThreshold())
|
||||
.append("powerSetMultiplier", getPowerSetMultiplier())
|
||||
.append("protectInterveneEnable", getProtectInterveneEnable())
|
||||
.append("protectL1DeratePercent", getProtectL1DeratePercent())
|
||||
.append("protectRecoveryStableSeconds", getProtectRecoveryStableSeconds())
|
||||
.append("protectL3LatchEnable", getProtectL3LatchEnable())
|
||||
.append("protectConflictPolicy", getProtectConflictPolicy())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@ -35,6 +35,14 @@ public class EmsStrategyTempTimeConfig extends BaseEntity
|
||||
@Excel(name = "充放功率 (kW)")
|
||||
private BigDecimal chargeDischargePower;
|
||||
|
||||
/** SDC下限 (%) */
|
||||
@Excel(name = "SDC下限 (%)")
|
||||
private BigDecimal sdcDown;
|
||||
|
||||
/** SDC上限 (%) */
|
||||
@Excel(name = "SDC上限 (%)")
|
||||
private BigDecimal sdcUp;
|
||||
|
||||
/** 充电状态,如“1-充电”、“2-待机” */
|
||||
@Excel(name = "充电状态,如“1-充电”、“2-待机”")
|
||||
private String chargeStatus;
|
||||
@ -83,6 +91,26 @@ public class EmsStrategyTempTimeConfig extends BaseEntity
|
||||
return chargeDischargePower;
|
||||
}
|
||||
|
||||
public void setSdcDown(BigDecimal sdcDown)
|
||||
{
|
||||
this.sdcDown = sdcDown;
|
||||
}
|
||||
|
||||
public BigDecimal getSdcDown()
|
||||
{
|
||||
return sdcDown;
|
||||
}
|
||||
|
||||
public void setSdcUp(BigDecimal sdcUp)
|
||||
{
|
||||
this.sdcUp = sdcUp;
|
||||
}
|
||||
|
||||
public BigDecimal getSdcUp()
|
||||
{
|
||||
return sdcUp;
|
||||
}
|
||||
|
||||
public void setChargeStatus(String chargeStatus)
|
||||
{
|
||||
this.chargeStatus = chargeStatus;
|
||||
@ -110,6 +138,8 @@ public class EmsStrategyTempTimeConfig extends BaseEntity
|
||||
.append("startTime", getStartTime())
|
||||
.append("endTime", getEndTime())
|
||||
.append("chargeDischargePower", getChargeDischargePower())
|
||||
.append("sdcDown", getSdcDown())
|
||||
.append("sdcUp", getSdcUp())
|
||||
.append("chargeStatus", getChargeStatus())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
package com.xzzn.ems.domain;
|
||||
|
||||
import com.xzzn.common.core.domain.BaseEntity;
|
||||
|
||||
public class SysBizRemark extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
|
||||
private String bizType;
|
||||
|
||||
private String bizKey1;
|
||||
|
||||
private String bizKey2;
|
||||
|
||||
private String delFlag;
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getBizType()
|
||||
{
|
||||
return bizType;
|
||||
}
|
||||
|
||||
public void setBizType(String bizType)
|
||||
{
|
||||
this.bizType = bizType;
|
||||
}
|
||||
|
||||
public String getBizKey1()
|
||||
{
|
||||
return bizKey1;
|
||||
}
|
||||
|
||||
public void setBizKey1(String bizKey1)
|
||||
{
|
||||
this.bizKey1 = bizKey1;
|
||||
}
|
||||
|
||||
public String getBizKey2()
|
||||
{
|
||||
return bizKey2;
|
||||
}
|
||||
|
||||
public void setBizKey2(String bizKey2)
|
||||
{
|
||||
this.bizKey2 = bizKey2;
|
||||
}
|
||||
|
||||
public String getDelFlag()
|
||||
{
|
||||
return delFlag;
|
||||
}
|
||||
|
||||
public void setDelFlag(String delFlag)
|
||||
{
|
||||
this.delFlag = delFlag;
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,15 @@ public class AmmeterRevenueStatisListVo {
|
||||
/** 类别 */
|
||||
private String dataTime;
|
||||
|
||||
/** 是否工作日:1-工作日 0-节假日 */
|
||||
private Integer isWorkday;
|
||||
|
||||
/** 日期类型 */
|
||||
private String dayType;
|
||||
|
||||
/** 天气情况 */
|
||||
private String weatherDesc;
|
||||
|
||||
/** 组合有功-总 */
|
||||
private BigDecimal activeTotalPrice = BigDecimal.ZERO;
|
||||
|
||||
@ -41,6 +50,11 @@ public class AmmeterRevenueStatisListVo {
|
||||
/** 组合无功-谷 */
|
||||
private BigDecimal reActiveValleyPrice = BigDecimal.ZERO;
|
||||
|
||||
/** 实际收益 */
|
||||
private BigDecimal actualRevenue = BigDecimal.ZERO;
|
||||
|
||||
private String remark;
|
||||
|
||||
public String getDataTime() {
|
||||
return dataTime;
|
||||
}
|
||||
@ -49,6 +63,30 @@ public class AmmeterRevenueStatisListVo {
|
||||
this.dataTime = dataTime;
|
||||
}
|
||||
|
||||
public Integer getIsWorkday() {
|
||||
return isWorkday;
|
||||
}
|
||||
|
||||
public void setIsWorkday(Integer isWorkday) {
|
||||
this.isWorkday = isWorkday;
|
||||
}
|
||||
|
||||
public String getDayType() {
|
||||
return dayType;
|
||||
}
|
||||
|
||||
public void setDayType(String dayType) {
|
||||
this.dayType = dayType;
|
||||
}
|
||||
|
||||
public String getWeatherDesc() {
|
||||
return weatherDesc;
|
||||
}
|
||||
|
||||
public void setWeatherDesc(String weatherDesc) {
|
||||
this.weatherDesc = weatherDesc;
|
||||
}
|
||||
|
||||
public BigDecimal getActiveTotalPrice() {
|
||||
return activeTotalPrice;
|
||||
}
|
||||
@ -128,4 +166,20 @@ public class AmmeterRevenueStatisListVo {
|
||||
public void setReActiveValleyPrice(BigDecimal reActiveValleyPrice) {
|
||||
this.reActiveValleyPrice = reActiveValleyPrice;
|
||||
}
|
||||
|
||||
public BigDecimal getActualRevenue() {
|
||||
return actualRevenue;
|
||||
}
|
||||
|
||||
public void setActualRevenue(BigDecimal actualRevenue) {
|
||||
this.actualRevenue = actualRevenue;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ public class AmmeterStatisListVo {
|
||||
/** 类别 */
|
||||
private String dataTime;
|
||||
|
||||
private String timePart;
|
||||
// private String timePart;
|
||||
|
||||
/** 组合有功-总 (kWh) */
|
||||
private BigDecimal activeTotalKwh = BigDecimal.ZERO;
|
||||
@ -46,6 +46,8 @@ public class AmmeterStatisListVo {
|
||||
/** 效率-有功总/无功总 */
|
||||
private BigDecimal effect = BigDecimal.ZERO;
|
||||
|
||||
private String remark;
|
||||
|
||||
public String getDataTime() {
|
||||
return dataTime;
|
||||
}
|
||||
@ -53,14 +55,14 @@ public class AmmeterStatisListVo {
|
||||
public void setDataTime(String dataTime) {
|
||||
this.dataTime = dataTime;
|
||||
}
|
||||
|
||||
public String getTimePart() {
|
||||
return timePart;
|
||||
}
|
||||
|
||||
public void setTimePart(String timePart) {
|
||||
this.timePart = timePart;
|
||||
}
|
||||
//
|
||||
// public String getTimePart() {
|
||||
// return timePart;
|
||||
// }
|
||||
//
|
||||
// public void setTimePart(String timePart) {
|
||||
// this.timePart = timePart;
|
||||
// }
|
||||
|
||||
public BigDecimal getActiveTotalKwh() {
|
||||
return activeTotalKwh;
|
||||
@ -149,4 +151,12 @@ public class AmmeterStatisListVo {
|
||||
public void setEffect(BigDecimal effect) {
|
||||
this.effect = effect;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,18 @@ public class BatteryDataStatsListVo {
|
||||
|
||||
/** SOH (%) */
|
||||
private BigDecimal soh;
|
||||
|
||||
/** 电压映射点位ID */
|
||||
private String voltagePointId;
|
||||
|
||||
/** 温度映射点位ID */
|
||||
private String temperaturePointId;
|
||||
|
||||
/** SOC映射点位ID */
|
||||
private String socPointId;
|
||||
|
||||
/** SOH映射点位ID */
|
||||
private String sohPointId;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date dataTimestamp;
|
||||
@ -71,6 +83,38 @@ public class BatteryDataStatsListVo {
|
||||
this.soh = soh;
|
||||
}
|
||||
|
||||
public String getVoltagePointId() {
|
||||
return voltagePointId;
|
||||
}
|
||||
|
||||
public void setVoltagePointId(String voltagePointId) {
|
||||
this.voltagePointId = voltagePointId;
|
||||
}
|
||||
|
||||
public String getTemperaturePointId() {
|
||||
return temperaturePointId;
|
||||
}
|
||||
|
||||
public void setTemperaturePointId(String temperaturePointId) {
|
||||
this.temperaturePointId = temperaturePointId;
|
||||
}
|
||||
|
||||
public String getSocPointId() {
|
||||
return socPointId;
|
||||
}
|
||||
|
||||
public void setSocPointId(String socPointId) {
|
||||
this.socPointId = socPointId;
|
||||
}
|
||||
|
||||
public String getSohPointId() {
|
||||
return sohPointId;
|
||||
}
|
||||
|
||||
public void setSohPointId(String sohPointId) {
|
||||
this.sohPointId = sohPointId;
|
||||
}
|
||||
|
||||
public Date getDataTimestamp() {
|
||||
return dataTimestamp;
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BizRemarkBatchGetRequest
|
||||
{
|
||||
private String bizType;
|
||||
|
||||
private String bizKey1;
|
||||
|
||||
private List<String> bizKey2List;
|
||||
|
||||
public String getBizType()
|
||||
{
|
||||
return bizType;
|
||||
}
|
||||
|
||||
public void setBizType(String bizType)
|
||||
{
|
||||
this.bizType = bizType;
|
||||
}
|
||||
|
||||
public String getBizKey1()
|
||||
{
|
||||
return bizKey1;
|
||||
}
|
||||
|
||||
public void setBizKey1(String bizKey1)
|
||||
{
|
||||
this.bizKey1 = bizKey1;
|
||||
}
|
||||
|
||||
public List<String> getBizKey2List()
|
||||
{
|
||||
return bizKey2List;
|
||||
}
|
||||
|
||||
public void setBizKey2List(List<String> bizKey2List)
|
||||
{
|
||||
this.bizKey2List = bizKey2List;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
public class BizRemarkSaveRequest
|
||||
{
|
||||
private String bizType;
|
||||
|
||||
private String bizKey1;
|
||||
|
||||
private String bizKey2;
|
||||
|
||||
private String remark;
|
||||
|
||||
public String getBizType()
|
||||
{
|
||||
return bizType;
|
||||
}
|
||||
|
||||
public void setBizType(String bizType)
|
||||
{
|
||||
this.bizType = bizType;
|
||||
}
|
||||
|
||||
public String getBizKey1()
|
||||
{
|
||||
return bizKey1;
|
||||
}
|
||||
|
||||
public void setBizKey1(String bizKey1)
|
||||
{
|
||||
this.bizKey1 = bizKey1;
|
||||
}
|
||||
|
||||
public String getBizKey2()
|
||||
{
|
||||
return bizKey2;
|
||||
}
|
||||
|
||||
public void setBizKey2(String bizKey2)
|
||||
{
|
||||
this.bizKey2 = bizKey2;
|
||||
}
|
||||
|
||||
public String getRemark()
|
||||
{
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark)
|
||||
{
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
@ -24,11 +24,25 @@ public class DevicePointDataList
|
||||
private BigDecimal avgValue;
|
||||
// 差值(max - min)
|
||||
private BigDecimal diffValue;
|
||||
// 第一四分位数
|
||||
private BigDecimal q1;
|
||||
// 中位数
|
||||
private BigDecimal median;
|
||||
// 第三四分位数
|
||||
private BigDecimal q3;
|
||||
|
||||
public DevicePointDataList(String deviceId, List<GeneralQueryDataVo> pointValueList,String parentDeviceId,
|
||||
BigDecimal maxValue, BigDecimal minValue,
|
||||
BigDecimal avgValue, BigDecimal diffValue,
|
||||
String maxDate, String minDate) {
|
||||
this(deviceId, pointValueList, parentDeviceId, maxValue, minValue, avgValue, diffValue, maxDate, minDate, null, null, null);
|
||||
}
|
||||
|
||||
public DevicePointDataList(String deviceId, List<GeneralQueryDataVo> pointValueList,String parentDeviceId,
|
||||
BigDecimal maxValue, BigDecimal minValue,
|
||||
BigDecimal avgValue, BigDecimal diffValue,
|
||||
String maxDate, String minDate,
|
||||
BigDecimal q1, BigDecimal median, BigDecimal q3) {
|
||||
this.deviceId = deviceId;
|
||||
this.pointValueList = pointValueList;
|
||||
this.parentDeviceId = parentDeviceId;
|
||||
@ -38,6 +52,9 @@ public class DevicePointDataList
|
||||
this.diffValue = diffValue;
|
||||
this.maxDate = maxDate;
|
||||
this.minDate = minDate;
|
||||
this.q1 = q1;
|
||||
this.median = median;
|
||||
this.q3 = q3;
|
||||
}
|
||||
|
||||
public DevicePointDataList(String deviceId, String parentDeviceId, List<GeneralQueryDataVo> pointValueList) {
|
||||
@ -121,4 +138,28 @@ public class DevicePointDataList
|
||||
public void setDiffValue(BigDecimal diffValue) {
|
||||
this.diffValue = diffValue;
|
||||
}
|
||||
|
||||
public BigDecimal getQ1() {
|
||||
return q1;
|
||||
}
|
||||
|
||||
public void setQ1(BigDecimal q1) {
|
||||
this.q1 = q1;
|
||||
}
|
||||
|
||||
public BigDecimal getMedian() {
|
||||
return median;
|
||||
}
|
||||
|
||||
public void setMedian(BigDecimal median) {
|
||||
this.median = median;
|
||||
}
|
||||
|
||||
public BigDecimal getQ3() {
|
||||
return q3;
|
||||
}
|
||||
|
||||
public void setQ3(BigDecimal q3) {
|
||||
this.q3 = q3;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package com.xzzn.ems.domain.vo;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 点位清单导出参数
|
||||
@ -46,6 +47,18 @@ public class DevicePointMatchExportVo implements Serializable {
|
||||
@Excel(name = "寄存器地址")
|
||||
private String ipAddress;
|
||||
|
||||
/** 二次项系数 */
|
||||
@Excel(name = "a")
|
||||
private BigDecimal a;
|
||||
|
||||
/** 一次项系数 */
|
||||
@Excel(name = "k")
|
||||
private BigDecimal k;
|
||||
|
||||
/** 常量 */
|
||||
@Excel(name = "b")
|
||||
private BigDecimal b;
|
||||
|
||||
public String getPointName() {
|
||||
return pointName;
|
||||
}
|
||||
@ -118,4 +131,27 @@ public class DevicePointMatchExportVo implements Serializable {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public BigDecimal getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(BigDecimal a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public BigDecimal getK() {
|
||||
return k;
|
||||
}
|
||||
|
||||
public void setK(BigDecimal k) {
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
public BigDecimal getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setB(BigDecimal b) {
|
||||
this.b = b;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package com.xzzn.ems.domain.vo;
|
||||
import com.xzzn.common.annotation.Excel;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 点位清单上传参数
|
||||
@ -46,6 +47,18 @@ public class DevicePointMatchVo implements Serializable {
|
||||
@Excel(name = "寄存器地址")
|
||||
private String ipAddress;
|
||||
|
||||
/** 二次项系数 */
|
||||
@Excel(name = "a")
|
||||
private BigDecimal a;
|
||||
|
||||
/** 一次项系数 */
|
||||
@Excel(name = "k")
|
||||
private BigDecimal k;
|
||||
|
||||
/** 常量 */
|
||||
@Excel(name = "b")
|
||||
private BigDecimal b;
|
||||
|
||||
/** 错误信息 */
|
||||
@Excel(name = "错误信息")
|
||||
private String errorMsg;
|
||||
@ -122,6 +135,30 @@ public class DevicePointMatchVo implements Serializable {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public BigDecimal getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA(BigDecimal a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public BigDecimal getK() {
|
||||
return k;
|
||||
}
|
||||
|
||||
public void setK(BigDecimal k) {
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
public BigDecimal getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setB(BigDecimal b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
public String getErrorMsg() {
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
@ -16,8 +16,9 @@ public class DeviceUpdateRequest {
|
||||
@NotBlank(message = "设备ID不能为空")
|
||||
private String deviceId;
|
||||
|
||||
/** 设备状态:0-离线、1-待机、2-运行、3-故障、4-停机 */
|
||||
private String deviceStatus;
|
||||
/** 工作状态:0-运行 1-停机 2-故障 */
|
||||
@NotBlank(message = "工作状态状态不能为空")
|
||||
private String workStatus;
|
||||
|
||||
/** 设备类型 */
|
||||
private String deviceCategory;
|
||||
@ -38,12 +39,12 @@ public class DeviceUpdateRequest {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDeviceStatus() {
|
||||
return deviceStatus;
|
||||
public String getWorkStatus() {
|
||||
return workStatus;
|
||||
}
|
||||
|
||||
public void setDeviceStatus(String deviceStatus) {
|
||||
this.deviceStatus = deviceStatus;
|
||||
public void setWorkStatus(String workStatus) {
|
||||
this.workStatus = workStatus;
|
||||
}
|
||||
|
||||
public String getDeviceCategory() {
|
||||
@ -53,4 +54,5 @@ public class DeviceUpdateRequest {
|
||||
public void setDeviceCategory(String deviceCategory) {
|
||||
this.deviceCategory = deviceCategory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
||||
import com.xzzn.ems.domain.EmsPcsSetting;
|
||||
|
||||
public class DevicesSettingVo extends EmsDevicesSetting
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** PCS设备配置 */
|
||||
private EmsPcsSetting pcsSetting;
|
||||
|
||||
public EmsPcsSetting getPcsSetting() {
|
||||
return pcsSetting;
|
||||
}
|
||||
|
||||
public void setPcsSetting(EmsPcsSetting pcsSetting) {
|
||||
this.pcsSetting = pcsSetting;
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 实时运行-有功无功功率数据
|
||||
@ -31,6 +30,8 @@ public class EnergyStoragePowVo {
|
||||
|
||||
private String deviceId;
|
||||
|
||||
private String groupTime;
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
@ -67,6 +68,14 @@ public class EnergyStoragePowVo {
|
||||
this.dateDay = dateDay;
|
||||
}
|
||||
|
||||
public String getGroupTime() {
|
||||
return groupTime;
|
||||
}
|
||||
|
||||
public void setGroupTime(String groupTime) {
|
||||
this.groupTime = groupTime;
|
||||
}
|
||||
|
||||
public void setPcsTotalReactivePower(BigDecimal pcsTotalReactivePower) {
|
||||
this.pcsTotalReactivePower = pcsTotalReactivePower;
|
||||
}
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
public class GeneralQueryPointOptionVo {
|
||||
private String pointId;
|
||||
private String pointName;
|
||||
private String dataKey;
|
||||
private String pointDesc;
|
||||
|
||||
public String getPointId() {
|
||||
return pointId;
|
||||
}
|
||||
|
||||
public void setPointId(String pointId) {
|
||||
this.pointId = pointId;
|
||||
}
|
||||
|
||||
public String getPointName() {
|
||||
return pointName;
|
||||
}
|
||||
|
||||
public void setPointName(String pointName) {
|
||||
this.pointName = pointName;
|
||||
}
|
||||
|
||||
public String getDataKey() {
|
||||
return dataKey;
|
||||
}
|
||||
|
||||
public void setDataKey(String dataKey) {
|
||||
this.dataKey = dataKey;
|
||||
}
|
||||
|
||||
public String getPointDesc() {
|
||||
return pointDesc;
|
||||
}
|
||||
|
||||
public void setPointDesc(String pointDesc) {
|
||||
this.pointDesc = pointDesc;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 按站点导入点位模板请求参数
|
||||
*/
|
||||
public class ImportPointTemplateRequest {
|
||||
|
||||
/** 目标站点ID */
|
||||
@NotBlank(message = "站点ID不能为空")
|
||||
private String siteId;
|
||||
|
||||
/** 是否覆盖目标站点已有点位配置 */
|
||||
private Boolean overwrite;
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public Boolean getOverwrite() {
|
||||
return overwrite;
|
||||
}
|
||||
|
||||
public void setOverwrite(Boolean overwrite) {
|
||||
this.overwrite = overwrite;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
public class PointConfigCurveRequest {
|
||||
private String siteId;
|
||||
private String deviceId;
|
||||
private String pointId;
|
||||
private String dataKey;
|
||||
private String pointType;
|
||||
private String rangeType;
|
||||
private String startTime;
|
||||
private String endTime;
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getPointId() {
|
||||
return pointId;
|
||||
}
|
||||
|
||||
public void setPointId(String pointId) {
|
||||
this.pointId = pointId;
|
||||
}
|
||||
|
||||
public String getDataKey() {
|
||||
return dataKey;
|
||||
}
|
||||
|
||||
public void setDataKey(String dataKey) {
|
||||
this.dataKey = dataKey;
|
||||
}
|
||||
|
||||
public String getPointType() {
|
||||
return pointType;
|
||||
}
|
||||
|
||||
public void setPointType(String pointType) {
|
||||
this.pointType = pointType;
|
||||
}
|
||||
|
||||
public String getRangeType() {
|
||||
return rangeType;
|
||||
}
|
||||
|
||||
public void setRangeType(String rangeType) {
|
||||
this.rangeType = rangeType;
|
||||
}
|
||||
|
||||
public String getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(String startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public String getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setEndTime(String endTime) {
|
||||
this.endTime = endTime;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class PointConfigCurveValueVo {
|
||||
private Date dataTime;
|
||||
private Object pointValue;
|
||||
|
||||
public Date getDataTime() {
|
||||
return dataTime;
|
||||
}
|
||||
|
||||
public void setDataTime(Date dataTime) {
|
||||
this.dataTime = dataTime;
|
||||
}
|
||||
|
||||
public Object getPointValue() {
|
||||
return pointValue;
|
||||
}
|
||||
|
||||
public void setPointValue(Object pointValue) {
|
||||
this.pointValue = pointValue;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
public class PointConfigGenerateRecentRequest {
|
||||
|
||||
@NotBlank(message = "站点ID不能为空")
|
||||
private String siteId;
|
||||
|
||||
@NotBlank(message = "点位ID不能为空")
|
||||
private String pointId;
|
||||
|
||||
private String deviceId;
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getPointId() {
|
||||
return pointId;
|
||||
}
|
||||
|
||||
public void setPointId(String pointId) {
|
||||
this.pointId = pointId;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
public class PointConfigLatestValueItemVo {
|
||||
private String siteId;
|
||||
private String pointId;
|
||||
private String deviceId;
|
||||
private String dataKey;
|
||||
|
||||
public String getSiteId() {
|
||||
return siteId;
|
||||
}
|
||||
|
||||
public void setSiteId(String siteId) {
|
||||
this.siteId = siteId;
|
||||
}
|
||||
|
||||
public String getPointId() {
|
||||
return pointId;
|
||||
}
|
||||
|
||||
public void setPointId(String pointId) {
|
||||
this.pointId = pointId;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getDataKey() {
|
||||
return dataKey;
|
||||
}
|
||||
|
||||
public void setDataKey(String dataKey) {
|
||||
this.dataKey = dataKey;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.xzzn.ems.domain.vo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PointConfigLatestValueRequest {
|
||||
private List<PointConfigLatestValueItemVo> points;
|
||||
|
||||
public List<PointConfigLatestValueItemVo> getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
public void setPoints(List<PointConfigLatestValueItemVo> points) {
|
||||
this.points = points;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user