From b51ece023e0739a1f80c12231542ac994aaae641 Mon Sep 17 00:00:00 2001 From: dengzedong <dengzedong@email> Date: 星期四, 10 四月 2025 14:54:48 +0800 Subject: [PATCH] 数据分析 添加 原始T+L预测值(未经过自动调整) 存储t+l_bak记录 自动调整功能 --- iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/dao/MmPredictAutoAdjustConfigDao.java | 27 ++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultLastPointServiceImpl.java | 9 iailab-module-model/iailab-module-model-biz/src/main/resources/mapper/pre/MmPredictAutoAdjustConfigDao.xml | 22 + iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/enums/DataMeasurement.java | 1 iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/pojo/InfluxModelResultLastBakSimPOJO.java | 18 + iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/vo/MmPredictAutoAdjustConfigVO.java | 92 +++++++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/common/enums/DataTypeEnum.java | 2 iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/McsApi.java | 4 iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/utils/MeasurementUtils.java | 2 iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/enums/AutoAdjustTriggerRuleEnum.java | 30 ++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/api/McsApiImpl.java | 8 iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/controller/admin/MmPredictAutoAdjustConfigController.java | 79 ++++++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultServiceImpl.java | 12 iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/dto/PreDataViewRespDTO.java | 3 iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/MmItemResultLastPointService.java | 4 iailab-module-model/iailab-module-model-biz/db/mysql.sql | 17 + iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/vo/MmPredictAutoAdjustConfigPageReqVO.java | 25 + iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmPredictAutoAdjustConfigServiceImpl.java | 216 ++++++++++++++++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/enums/AutoAdjustValueRuleEnum.java | 54 ++++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/entity/MmPredictAutoAdjustConfigEntity.java | 87 ++++++ iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/MmPredictAutoAdjustConfigService.java | 32 ++ iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/dto/MmPredictAutoAdjustReqVO.java | 15 + 22 files changed, 755 insertions(+), 4 deletions(-) diff --git a/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/McsApi.java b/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/McsApi.java index 1a9fb2f..eb16320 100644 --- a/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/McsApi.java +++ b/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/McsApi.java @@ -161,4 +161,8 @@ @Operation(summary = "获取15分钟内最新预警建议信息") List<ScheduleSuggestRespDTO> getLastSuggest(@RequestParam Map<String, Object> params); + @PostMapping(PREFIX + "/predict-item/autoAdjustByCode") + @Operation(summary = "自动调整") + Boolean autoAdjustByCode(@RequestBody MmPredictAutoAdjustReqVO reqVO); + } \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/dto/MmPredictAutoAdjustReqVO.java b/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/dto/MmPredictAutoAdjustReqVO.java new file mode 100644 index 0000000..d717a6e --- /dev/null +++ b/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/dto/MmPredictAutoAdjustReqVO.java @@ -0,0 +1,15 @@ +package com.iailab.module.model.api.mcs.dto; + +import lombok.Data; + +/** + * @description: + * @author: dzd + * @date: 2025/4/9 16:15 + **/ +@Data +public class MmPredictAutoAdjustReqVO { + + private String configCode; + private Long adjustStartTime; +} \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/dto/PreDataViewRespDTO.java b/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/dto/PreDataViewRespDTO.java index 40662a2..63e1d89 100644 --- a/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/dto/PreDataViewRespDTO.java +++ b/iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/dto/PreDataViewRespDTO.java @@ -111,6 +111,9 @@ @Schema(description = "T+L预测值,L表示预测长度") private List<Object[]> preDataL; + @Schema(description = "原始T+L预测值(未经过自动调整)") + private List<Object[]> preDataLOriginal; + @Schema(description = "当时预测值") private List<Object[]> curData; diff --git a/iailab-module-model/iailab-module-model-biz/db/mysql.sql b/iailab-module-model/iailab-module-model-biz/db/mysql.sql index e49a221..a0a416a 100644 --- a/iailab-module-model/iailab-module-model-biz/db/mysql.sql +++ b/iailab-module-model/iailab-module-model-biz/db/mysql.sql @@ -1011,3 +1011,20 @@ -- t_st_schedule_record 创建复合索引 CREATE INDEX idx_st_schedule_record_scheme_result_time ON t_st_schedule_record (scheme_id, result_code, schedule_time DESC, create_time DESC); + +CREATE TABLE `t_mm_predict_auto_adjust_config` ( + `id` varchar(36) NOT NULL, + `config_name` varchar(255) DEFAULT NULL COMMENT '配置名称', + `config_code` varchar(255) DEFAULT NULL COMMENT '配置编码', + `output_id` varchar(36) DEFAULT NULL COMMENT '预测输出id', + `point_id` varchar(36) DEFAULT NULL COMMENT '测点id', + `t` int DEFAULT NULL COMMENT '取值时间范围', + `trigger_rule` varchar(36) DEFAULT NULL COMMENT '触发规则', + `trigger_value` double DEFAULT NULL COMMENT '触发值', + `adjust_direction` tinyint DEFAULT '1' COMMENT '调整方向(1:正向,-1:反向)', + `adjust_length` int DEFAULT NULL COMMENT '调整长度', + `adjust_value_rule` varchar(36) DEFAULT NULL COMMENT '调整值计算规则', + `is_enable` tinyint DEFAULT NULL COMMENT '是否启用(0禁用 1启用)', + `create_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='预测结果自动调整配置表'; diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/api/McsApiImpl.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/api/McsApiImpl.java index 268ec05..b63180e 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/api/McsApiImpl.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/api/McsApiImpl.java @@ -119,7 +119,7 @@ private StScheduleModelSettingService stScheduleModelSettingService; @Autowired - private StAdjustConfigService stAdjustConfigService; + private MmPredictAutoAdjustConfigService autoAdjustService; private int HOUR_MINS = 60; @Override @@ -219,6 +219,7 @@ viewDto.setRealData(getHisData(output.getPointid(), startTime, endTime, reqVO.getPrec())); viewDto.setPreDataN(mmItemResultService.getData(output.getId(), startTime, endTime, DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)); viewDto.setPreDataL(mmItemResultLastPointService.getData(output.getId(), startTime, endTime, DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)); + viewDto.setPreDataLOriginal(mmItemResultLastPointService.getData(output.getId(), startTime, endTime, DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND,DataTypeEnum.FLOAT_LAST_BAK)); viewDto.setCurData(mmItemResultJsonService.getData(output.getId(), predictTime, DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)); // 模拟调整曲线 viewDto.setAdjData(stAdjustResultService.getData(output.getId(), predictTime, DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)); @@ -913,6 +914,11 @@ return stScheduleSuggestService.getLastSuggest(params); } + @Override + public Boolean autoAdjustByCode(MmPredictAutoAdjustReqVO reqVO) { + return autoAdjustService.autoAdjustByCode(reqVO.getConfigCode(),reqVO.getAdjustStartTime()); + } + private Date[] calResultTime(ItemVO predictItem, Date startTimeReq, Date endTimeReq, int lengthLeft, int lengthRight) { Date[] result = new Date[3]; Date predictTime = predictItem.getLastTime(); diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/common/enums/DataTypeEnum.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/common/enums/DataTypeEnum.java index 2d6deb1..070e2d7 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/common/enums/DataTypeEnum.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/common/enums/DataTypeEnum.java @@ -18,6 +18,8 @@ FLOAT_LAST("float_last"), + FLOAT_LAST_BAK("float_last_bak"), + BOOLEAN("boolean"); private String code; diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/enums/DataMeasurement.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/enums/DataMeasurement.java index 7e2fb65..4eb9496 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/enums/DataMeasurement.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/enums/DataMeasurement.java @@ -10,6 +10,7 @@ public enum DataMeasurement { t_md_sim_value, t_md_last_sim_value, + t_md_last_bak_sim_value, t_md_dig_value, t_md_bool_value, t_md_str_value, diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/utils/MeasurementUtils.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/utils/MeasurementUtils.java index 6e54db1..66a59dd 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/utils/MeasurementUtils.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/common/utils/MeasurementUtils.java @@ -19,6 +19,8 @@ return DataMeasurement.t_md_bool_value.name(); } else if (DataTypeEnum.FLOAT_LAST.getCode().equals(type)) { return DataMeasurement.t_md_last_sim_value.name(); + } else if (DataTypeEnum.FLOAT_LAST_BAK.getCode().equals(type)) { + return DataMeasurement.t_md_last_bak_sim_value.name(); } else { return DataMeasurement.t_md_str_value.name(); } diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/pojo/InfluxModelResultLastBakSimPOJO.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/pojo/InfluxModelResultLastBakSimPOJO.java new file mode 100644 index 0000000..a9dbc7d --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/influxdb/pojo/InfluxModelResultLastBakSimPOJO.java @@ -0,0 +1,18 @@ +package com.iailab.module.model.influxdb.pojo; + +import com.influxdb.annotations.Column; +import com.influxdb.annotations.Measurement; +import lombok.Data; + +/** + * @description: + * @author: dzd + * @date: 2025/1/7 13:43 + **/ +@Data +@Measurement(name = "t_md_last_bak_sim_value") +public class InfluxModelResultLastBakSimPOJO extends InfluxModelResultPOJO { + + @Column + private Double value; +} diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/controller/admin/MmPredictAutoAdjustConfigController.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/controller/admin/MmPredictAutoAdjustConfigController.java new file mode 100644 index 0000000..827a6ff --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/controller/admin/MmPredictAutoAdjustConfigController.java @@ -0,0 +1,79 @@ +package com.iailab.module.model.mcs.pre.controller.admin; + +import com.iailab.framework.common.pojo.CommonResult; +import com.iailab.framework.common.pojo.PageResult; +import com.iailab.framework.common.util.object.BeanUtils; +import com.iailab.module.model.api.mcs.McsApi; +import com.iailab.module.model.api.mcs.dto.MmPredictAutoAdjustReqVO; +import com.iailab.module.model.mcs.pre.entity.MmPredictAutoAdjustConfigEntity; +import com.iailab.module.model.mcs.pre.service.MmPredictAutoAdjustConfigService; +import com.iailab.module.model.mcs.pre.vo.MmPredictAutoAdjustConfigPageReqVO; +import com.iailab.module.model.mcs.pre.vo.MmPredictAutoAdjustConfigVO; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import static com.iailab.framework.common.pojo.CommonResult.success; + +/** + * @description: + * @author: dzd + * @date: 2025/4/8 13:30 + **/ +@RestController +@RequestMapping("/model/pre/auto-adjust-config") +public class MmPredictAutoAdjustConfigController { + + @Autowired + private MmPredictAutoAdjustConfigService autoAdjustConfigService; + + @Autowired + private McsApi mcsApi; + + @PostMapping("/create") + @Operation(summary = "创建自动调整配置") + @PreAuthorize("@ss.hasPermission('pre:auto-adjust-config:create')") + public CommonResult<Boolean> create(@RequestBody MmPredictAutoAdjustConfigEntity entity) { + autoAdjustConfigService.create(entity); + return success(true); + } + + @PutMapping("/update") + @Operation(summary = "更新自动调整配置") + @PreAuthorize("@ss.hasPermission('pre:auto-adjust-config:update')") + public CommonResult<Boolean> update(@RequestBody MmPredictAutoAdjustConfigEntity entity) { + autoAdjustConfigService.update(entity); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除自动调整配置") + @PreAuthorize("@ss.hasPermission('pre:auto-adjust-config:delete')") + public CommonResult<Boolean> delete(@RequestParam("id") String id) { + autoAdjustConfigService.delete(id); + return success(true); + } + + @GetMapping("/get/{id}") + @Operation(summary = "获得自动调整配置") + @PreAuthorize("@ss.hasPermission('pre:auto-adjust-config:query')") + public CommonResult<MmPredictAutoAdjustConfigVO> getInfo(@PathVariable("id") String id) { + MmPredictAutoAdjustConfigEntity entity = autoAdjustConfigService.getInfo(id); + return success(BeanUtils.toBean(entity, MmPredictAutoAdjustConfigVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得自动调整配置分页") + @PreAuthorize("@ss.hasPermission('pre:auto-adjust-config:query')") + public CommonResult<PageResult<MmPredictAutoAdjustConfigVO>> page(MmPredictAutoAdjustConfigPageReqVO params) { + PageResult<MmPredictAutoAdjustConfigVO> pageResult = autoAdjustConfigService.page(params); + return success(pageResult); + } + + @PostMapping("/autoAdjustByCode") + @Operation(summary = "通过configCode自动调整") + public Boolean autoAdjustByCode(@RequestBody MmPredictAutoAdjustReqVO params) { + return autoAdjustConfigService.autoAdjustByCode(params.getConfigCode(),params.getAdjustStartTime()); + } +} \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/dao/MmPredictAutoAdjustConfigDao.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/dao/MmPredictAutoAdjustConfigDao.java new file mode 100644 index 0000000..cd82fa9 --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/dao/MmPredictAutoAdjustConfigDao.java @@ -0,0 +1,27 @@ +package com.iailab.module.model.mcs.pre.dao; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.iailab.framework.mybatis.core.mapper.BaseMapperX; +import com.iailab.framework.tenant.core.db.dynamic.TenantDS; +import com.iailab.module.model.mcs.pre.entity.MmPredictAutoAdjustConfigEntity; +import com.iailab.module.model.mcs.pre.vo.MmPredictAlarmConfigRespVO; +import com.iailab.module.model.mcs.pre.vo.MmPredictAutoAdjustConfigPageReqVO; +import com.iailab.module.model.mcs.pre.vo.MmPredictAutoAdjustConfigVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @description: + * @author: dzd + * @date: 2025/4/8 14:16 + **/ +@TenantDS +@Mapper +public interface MmPredictAutoAdjustConfigDao extends BaseMapperX<MmPredictAutoAdjustConfigEntity> { + + IPage<MmPredictAutoAdjustConfigVO> getPage(IPage<MmPredictAutoAdjustConfigEntity> page, @Param("params") MmPredictAutoAdjustConfigPageReqVO reqVO); + + default IPage<MmPredictAutoAdjustConfigVO> selectPage(MmPredictAutoAdjustConfigPageReqVO params) { + return getPage(getPage(params), params); + } +} diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/entity/MmPredictAutoAdjustConfigEntity.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/entity/MmPredictAutoAdjustConfigEntity.java new file mode 100644 index 0000000..2365736 --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/entity/MmPredictAutoAdjustConfigEntity.java @@ -0,0 +1,87 @@ +package com.iailab.module.model.mcs.pre.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * @description: + * @author: dzd + * @date: 2025/4/8 11:59 + **/ +@Data +@TableName("t_mm_predict_auto_adjust_config") +public class MmPredictAutoAdjustConfigEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id",type = IdType.INPUT) + private String id; + + /** + * 配置名称 + */ + private String configName; + + /** + * 配置编码 + */ + private String configCode; + + /** + * 预测输出id + */ + private String outputId; + + /** + * 测点id + */ + private String pointId; + + /** + * 取值时间范围 + */ + private Integer t; + + /** + * 触发规则 + */ + private String triggerRule; + + /** + * 触发值 + */ + private Double triggerValue; + + /** + * 调整方向(1:正向,-1:反向) + */ + private Integer adjustDirection; + + /** + * 调整长度 + */ + private Integer adjustLength; + + /** + * 调整值计算规则 + */ + private String adjustValueRule; + + /** + * 是否启用(0禁用 1启用) + */ + private Integer isEnable; + + /** + * 创建时间 + */ + private Date createTime; +} \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/enums/AutoAdjustTriggerRuleEnum.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/enums/AutoAdjustTriggerRuleEnum.java new file mode 100644 index 0000000..05f9e9d --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/enums/AutoAdjustTriggerRuleEnum.java @@ -0,0 +1,30 @@ +package com.iailab.module.model.mcs.pre.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 自动调整 触发规则 + */ +@Getter +@AllArgsConstructor +public enum AutoAdjustTriggerRuleEnum { + + // 斜率 + SLOPE("slope"), + // 平均差 + AVERAGE_GAP("average_gap"), + UNKNOW("unknow"); + + + private final String code; + + public static AutoAdjustTriggerRuleEnum fromCode(String code) { + for (AutoAdjustTriggerRuleEnum unit : values()) { + if (unit.code.equals(code)) { + return unit; + } + } + return UNKNOW; + } +} diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/enums/AutoAdjustValueRuleEnum.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/enums/AutoAdjustValueRuleEnum.java new file mode 100644 index 0000000..e4b50d8 --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/enums/AutoAdjustValueRuleEnum.java @@ -0,0 +1,54 @@ +package com.iailab.module.model.mcs.pre.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.function.Function; + +/** + * 自动调整 调整值计算规则 + */ +@Getter +@AllArgsConstructor +public enum AutoAdjustValueRuleEnum { + + // 均值差 + AVERAGE_GAP_VALUE("average_gap_value",(map) -> { + if (!map.containsKey("startValue") || !map.containsKey("endValue")) { + return 0.0; + } + Double startValue = Double.valueOf(map.get("startValue").toString()); + Double endValue = Double.valueOf(map.get("endValue").toString()); + return endValue - startValue; + }); + + + private final String code; + private final Function<HashMap<String,Object>, Double> calculator; + + public Double calculate(HashMap<String,Object> map) { + return calculator.apply(map); + } + + public static AutoAdjustValueRuleEnum fromCode(String code) { + for (AutoAdjustValueRuleEnum unit : values()) { + if (unit.code.equals(code)) { + return unit; + } + } + return null; + } + + public static Double getAdjustValue(String code,HashMap<String,Object> map) { + + AutoAdjustValueRuleEnum rule = AutoAdjustValueRuleEnum.fromCode(code); + + if (rule == null) { + return 0.0; + } + return rule.calculate(map); + } +} diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/MmItemResultLastPointService.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/MmItemResultLastPointService.java index 89b1d62..8dbf524 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/MmItemResultLastPointService.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/MmItemResultLastPointService.java @@ -1,5 +1,7 @@ package com.iailab.module.model.mcs.pre.service; +import com.iailab.module.model.common.enums.DataTypeEnum; + import java.util.Date; import java.util.List; @@ -11,4 +13,6 @@ public interface MmItemResultLastPointService { List<Object[]> getData(String outputid, Date startTime, Date endTime, String timeFormat); + + List<Object[]> getData(String outputid, Date startTime, Date endTime, String timeFormat, DataTypeEnum dataTypeEnum); } diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/MmPredictAutoAdjustConfigService.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/MmPredictAutoAdjustConfigService.java new file mode 100644 index 0000000..b77d6e2 --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/MmPredictAutoAdjustConfigService.java @@ -0,0 +1,32 @@ +package com.iailab.module.model.mcs.pre.service; + +import com.iailab.framework.common.pojo.PageResult; +import com.iailab.framework.common.service.BaseService; +import com.iailab.module.model.mcs.pre.entity.MmPredictAutoAdjustConfigEntity; +import com.iailab.module.model.mcs.pre.vo.MmPredictAutoAdjustConfigPageReqVO; +import com.iailab.module.model.mcs.pre.vo.MmPredictAutoAdjustConfigVO; + +import java.util.List; +import java.util.Map; + +/** + * @description: + * @author: dzd + * @date: 2025/4/8 13:44 + **/ +public interface MmPredictAutoAdjustConfigService extends BaseService<MmPredictAutoAdjustConfigEntity> { + + PageResult<MmPredictAutoAdjustConfigVO> page(MmPredictAutoAdjustConfigPageReqVO params); + + MmPredictAutoAdjustConfigEntity getInfo(String id); + + MmPredictAutoAdjustConfigEntity getByCode(String code); + + void create(MmPredictAutoAdjustConfigEntity entity); + + void update(MmPredictAutoAdjustConfigEntity entity); + + void delete(String id); + + boolean autoAdjustByCode(String configCode,long adjustStartTime); +} diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultLastPointServiceImpl.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultLastPointServiceImpl.java index ff83a11..dd8a3d8 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultLastPointServiceImpl.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultLastPointServiceImpl.java @@ -26,9 +26,16 @@ @Override public List<Object[]> getData(String outputid, Date startTime, Date endTime, String timeFormat) { + return getData(outputid,startTime,endTime,timeFormat,null); + } + + public List<Object[]> getData(String outputid, Date startTime, Date endTime, String timeFormat,DataTypeEnum dataTypeEnum) { List<Object[]> result = new ArrayList<>(); InfluxModelResultPOJO pojo = new InfluxModelResultPOJO(); - pojo.setType(DataTypeEnum.FLOAT_LAST.getCode()); + if (dataTypeEnum == null) { + dataTypeEnum = DataTypeEnum.FLOAT_LAST; + } + pojo.setType(dataTypeEnum.getCode()); pojo.setOutPutId(outputid); List<InfluxModelResultVO> influxModelResultVOS = influxDBService.queryModelResults(pojo, startTime, endTime); influxModelResultVOS.forEach(item -> { diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultServiceImpl.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultServiceImpl.java index ce85a55..95a9aa0 100644 --- a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultServiceImpl.java +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmItemResultServiceImpl.java @@ -3,6 +3,7 @@ import com.alibaba.fastjson.JSONArray; import com.iailab.framework.common.util.date.DateUtils; import com.iailab.module.model.common.enums.DataTypeEnum; +import com.iailab.module.model.influxdb.pojo.InfluxModelResultLastBakSimPOJO; import com.iailab.module.model.influxdb.pojo.InfluxModelResultLastSimPOJO; import com.iailab.module.model.influxdb.pojo.InfluxModelResultPOJO; import com.iailab.module.model.influxdb.pojo.InfluxModelResultSimPOJO; @@ -36,6 +37,7 @@ public void savePredictValue(Map<String, List<DataValueVO>> predictValueMap, int t, String nIndex, Date predictTime) { List<InfluxModelResultPOJO> importList = new ArrayList<>(); List<InfluxModelResultPOJO> lastList = new ArrayList<>(); + List<InfluxModelResultPOJO> lastBakList = new ArrayList<>(); List<MmItemResultJsonEntity> resultJsonList = new ArrayList<>(); for (Map.Entry<String, List<DataValueVO>> entry : predictValueMap.entrySet()) { @@ -44,7 +46,6 @@ pojo.setValue(dataVo.getDataValue()); pojo.setTimestamp(dataVo.getDataTime().toInstant()); pojo.setOutPutId(entry.getKey()); - pojo.setType(DataTypeEnum.FLOAT.getCode()); importList.add(pojo); } @@ -67,8 +68,13 @@ pojo.setValue(dataVo.getDataValue()); pojo.setTimestamp(dataVo.getDataTime().toInstant()); pojo.setOutPutId(entry.getKey()); - pojo.setType(DataTypeEnum.FLOAT.getCode()); lastList.add(pojo); + + InfluxModelResultLastBakSimPOJO bakSimPojo = new InfluxModelResultLastBakSimPOJO(); + bakSimPojo.setValue(dataVo.getDataValue()); + bakSimPojo.setTimestamp(dataVo.getDataTime().toInstant()); + bakSimPojo.setOutPutId(entry.getKey()); + lastBakList.add(bakSimPojo); } MmItemResultJsonEntity resultJson = new MmItemResultJsonEntity(); @@ -85,6 +91,8 @@ // double结果存入influxdb influxDBService.asyncWriteModelResults(importList); influxDBService.asyncWriteModelResults(lastList); + // t+l备份 + influxDBService.asyncWriteModelResults(lastBakList); } @Override diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmPredictAutoAdjustConfigServiceImpl.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmPredictAutoAdjustConfigServiceImpl.java new file mode 100644 index 0000000..510b5f4 --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/service/impl/MmPredictAutoAdjustConfigServiceImpl.java @@ -0,0 +1,216 @@ +package com.iailab.module.model.mcs.pre.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.iailab.framework.common.pojo.PageResult; +import com.iailab.framework.common.service.impl.BaseServiceImpl; +import com.iailab.module.data.api.point.DataPointApi; +import com.iailab.module.data.api.point.dto.ApiPointValueDTO; +import com.iailab.module.data.api.point.dto.ApiPointValueQueryDTO; +import com.iailab.module.model.common.enums.DataTypeEnum; +import com.iailab.module.model.influxdb.pojo.InfluxModelResultLastSimPOJO; +import com.iailab.module.model.influxdb.pojo.InfluxModelResultPOJO; +import com.iailab.module.model.influxdb.service.InfluxDBService; +import com.iailab.module.model.influxdb.vo.InfluxModelResultVO; +import com.iailab.module.model.mcs.pre.dao.MmPredictAutoAdjustConfigDao; +import com.iailab.module.model.mcs.pre.entity.MmPredictAutoAdjustConfigEntity; +import com.iailab.module.model.mcs.pre.enums.AutoAdjustTriggerRuleEnum; +import com.iailab.module.model.mcs.pre.enums.AutoAdjustValueRuleEnum; +import com.iailab.module.model.mcs.pre.service.MmPredictAutoAdjustConfigService; +import com.iailab.module.model.mcs.pre.service.MmPredictItemService; +import com.iailab.module.model.mcs.pre.vo.MmPredictAutoAdjustConfigPageReqVO; +import com.iailab.module.model.mcs.pre.vo.MmPredictAutoAdjustConfigVO; +import com.iailab.module.model.mdk.vo.ItemVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author PanZhibao + * @Description + * @createTime 2024年11月19日 + */ +@Slf4j +@Service +public class MmPredictAutoAdjustConfigServiceImpl extends BaseServiceImpl<MmPredictAutoAdjustConfigDao, MmPredictAutoAdjustConfigEntity> + implements MmPredictAutoAdjustConfigService { + + @Autowired + private MmPredictItemService mmPredictItemService; + @Autowired + private InfluxDBService influxDBService; + @Autowired + private DataPointApi dataPointApi; + + @Override + public PageResult<MmPredictAutoAdjustConfigVO> page(MmPredictAutoAdjustConfigPageReqVO params) { + IPage<MmPredictAutoAdjustConfigVO> page = baseDao.selectPage(params); + return new PageResult<>(page.getRecords(), page.getTotal()); + } + + @Override + public MmPredictAutoAdjustConfigEntity getInfo(String id) { + return baseDao.selectById(id); + } + + @Override + public MmPredictAutoAdjustConfigEntity getByCode(String code) { + return baseDao.selectOne("config_code",code); + } + + @Override + public void create(MmPredictAutoAdjustConfigEntity entity) { + entity.setId(UUID.randomUUID().toString()); + baseDao.insert(entity); + } + + @Override + public void update(MmPredictAutoAdjustConfigEntity entity) { + baseDao.updateById(entity); + } + + @Override + public void delete(String id) { + baseDao.deleteById(id); + } + + @Override + public boolean autoAdjustByCode(String configCode,long adjustStartTime) { + // 查询调整配置 + MmPredictAutoAdjustConfigEntity configEntity = getByCode(configCode); + if (configEntity == null) { + log.info("自动调整失败原因:configEntity为null"); + return false; + } + + Double adjustValue = 0.0; + ApiPointValueQueryDTO queryDTO; + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(adjustStartTime); + Date endTime; + Date startTime; + List<ApiPointValueDTO> apiPointValueDTOS; + // 判断是否调整 + AutoAdjustTriggerRuleEnum triggerRuleEnum = AutoAdjustTriggerRuleEnum.fromCode(configEntity.getTriggerRule()); + switch (triggerRuleEnum) { + case SLOPE: + queryDTO = new ApiPointValueQueryDTO(); + queryDTO.setPointNo(dataPointApi.getInfoById(configEntity.getPointId()).getPointNo()); + endTime = calendar.getTime(); + queryDTO.setEnd(endTime); + calendar.add(Calendar.MINUTE,-1 * configEntity.getT()); + startTime = calendar.getTime(); + queryDTO.setStart(startTime); + apiPointValueDTOS = dataPointApi.queryPointHistoryValue(queryDTO); + if (CollectionUtils.isEmpty(apiPointValueDTOS)) { + log.info("自动调整失败原因:测点数据长度为0"); + return false; + } + apiPointValueDTOS = apiPointValueDTOS.stream().filter(e -> e.getV() != -2).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(apiPointValueDTOS)) { + log.info("自动调整失败原因:测点数据长度为0"); + return false; + } + Optional<ApiPointValueDTO> startOptional = apiPointValueDTOS.stream().filter(e -> e.getT().equals(startTime)).findFirst(); + if (!startOptional.isPresent()) { + log.info("自动调整失败原因:计算斜率startTime时间点测点值为null,startTime:" + startTime); + return false; + } + Optional<ApiPointValueDTO> endOptional = apiPointValueDTOS.stream().filter(e -> e.getT().equals(endTime)).findFirst(); + if (!endOptional.isPresent()) { + log.info("自动调整失败原因:计算斜率endTime时间点测点值为null,endTime:" + endTime); + return false; + } + ApiPointValueDTO startPointValue = startOptional.get(); + ApiPointValueDTO endPointValue = endOptional.get(); + + // 计算斜率,有正负之分,代表上升或下降 + double slope = BigDecimal.valueOf(endPointValue.getV() - startPointValue.getV()).divide(BigDecimal.valueOf(configEntity.getT())).doubleValue(); + //斜率绝对值大于等于触发值则进行调整 + if (Double.valueOf(Math.abs(slope)).compareTo(configEntity.getTriggerValue()) >= 0) { + //计算调整值 + HashMap<String,Object> map = new HashMap<>(); + map.put("startValue",startPointValue.getV()); + map.put("endValue",endPointValue.getV()); + adjustValue = AutoAdjustValueRuleEnum.getAdjustValue(configEntity.getAdjustValueRule(), map); + } else { + log.info("自动调整失败原因:斜率小于调整值,斜率:" + slope); + return false; + } + break; + case AVERAGE_GAP: + queryDTO = new ApiPointValueQueryDTO(); + queryDTO.setPointNo(dataPointApi.getInfoById(configEntity.getPointId()).getPointNo()); + endTime = calendar.getTime(); + queryDTO.setEnd(endTime); + calendar.add(Calendar.MINUTE,-1 * configEntity.getT() * 2 + 1); + startTime = calendar.getTime(); + queryDTO.setStart(startTime); + apiPointValueDTOS = dataPointApi.queryPointHistoryValue(queryDTO); + if (CollectionUtils.isEmpty(apiPointValueDTOS)) { + log.info("自动调整失败原因:测点数据长度为0"); + return false; + } + apiPointValueDTOS = apiPointValueDTOS.stream().filter(e -> e.getV() != -2).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(apiPointValueDTOS)) { + log.info("自动调整失败原因:测点数据长度为0"); + return false; + } + calendar.add(Calendar.MINUTE,configEntity.getT()); + double startAverage = apiPointValueDTOS.stream().filter(e -> e.getT().before(calendar.getTime())).collect(Collectors.summarizingDouble(ApiPointValueDTO::getV)).getAverage(); + double endAverage = apiPointValueDTOS.stream().filter(e -> e.getT().compareTo(calendar.getTime()) >= 0).collect(Collectors.summarizingDouble(ApiPointValueDTO::getV)).getAverage(); + // 计算均值差,大于等于触发值则进行调整 + if (Double.valueOf(Math.abs(startAverage - endAverage)).compareTo(configEntity.getTriggerValue()) >= 0) { + //计算调整值 + HashMap<String,Object> map = new HashMap<>(); + map.put("startValue",startAverage); + map.put("endValue",endAverage); + adjustValue = AutoAdjustValueRuleEnum.getAdjustValue(configEntity.getAdjustValueRule(), map); + } else { + log.info("自动调整失败原因:均值差小于调整值,均值差:" + (startAverage - endAverage)); + return false; + } + break; + default: + log.info("自动调整失败原因:未知触发规则,triggerRule" + configEntity.getTriggerRule()); + return false; + } + + // 调整方向 + adjustValue = adjustValue * configEntity.getAdjustDirection(); + + // 获取历史结果 + ItemVO item = mmPredictItemService.getItemByOutPutId(configEntity.getOutputId()); + if (item == null) { + log.info("自动调整失败原因:getItemByOutPutId为null,outputId:" + configEntity.getOutputId()); + return false; + } + Calendar resultCalendar = Calendar.getInstance(); + resultCalendar.setTimeInMillis(adjustStartTime); + Date resultStartTime = resultCalendar.getTime(); + resultCalendar.add(Calendar.SECOND,configEntity.getAdjustLength() * item.getGranularity()); + Date resultEndTime = resultCalendar.getTime(); + InfluxModelResultPOJO pojo = new InfluxModelResultPOJO(); + pojo.setType(DataTypeEnum.FLOAT_LAST_BAK.getCode()); + pojo.setOutPutId(configEntity.getOutputId()); + List<InfluxModelResultVO> influxModelResult = influxDBService.queryModelResults(pojo, resultStartTime, resultEndTime); + + List<InfluxModelResultPOJO> lastList = new ArrayList<>(); + for (InfluxModelResultVO resultVO : influxModelResult) { + InfluxModelResultLastSimPOJO adjustPojo = new InfluxModelResultLastSimPOJO(); + // 设置新的调整值 + adjustPojo.setValue(Double.parseDouble(resultVO.getValue().toString()) + adjustValue); + adjustPojo.setTimestamp(resultVO.getTimestamp()); + adjustPojo.setOutPutId(configEntity.getOutputId()); + lastList.add(adjustPojo); + } + // 相同时间直接覆盖旧值 + influxDBService.asyncWriteModelResults(lastList); + log.info("t+l自动调整。configCode:" + configCode + ",adjustValue:" + adjustValue + ",resultStartTime:" + resultStartTime + ",resultEndTime:" + resultEndTime + "调整长度:" + lastList.size()); + return true; + } +} \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/vo/MmPredictAutoAdjustConfigPageReqVO.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/vo/MmPredictAutoAdjustConfigPageReqVO.java new file mode 100644 index 0000000..54c5a94 --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/vo/MmPredictAutoAdjustConfigPageReqVO.java @@ -0,0 +1,25 @@ +package com.iailab.module.model.mcs.pre.vo; + +import com.iailab.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * @description: + * @author: dzd + * @date: 2025/4/8 14:35 + **/ +@Schema(description = "预测结果自动调整配置 - Page Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MmPredictAutoAdjustConfigPageReqVO extends PageParam { + + @Schema(description = "配置名称") + private String configName; + + @Schema(description = "配置编码") + private String configCode; +} \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/vo/MmPredictAutoAdjustConfigVO.java b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/vo/MmPredictAutoAdjustConfigVO.java new file mode 100644 index 0000000..3e9b855 --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/java/com/iailab/module/model/mcs/pre/vo/MmPredictAutoAdjustConfigVO.java @@ -0,0 +1,92 @@ +package com.iailab.module.model.mcs.pre.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * @description: + * @author: dzd + * @date: 2025/4/8 11:59 + **/ +@Data +public class MmPredictAutoAdjustConfigVO implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private String id; + + /** + * 配置名称 + */ + private String configName; + + /** + * 配置编码 + */ + private String configCode; + + /** + * 预测输出id + */ + private String outputId; + + /** + * 测点id + */ + private String pointId; + + /** + * 取值时间范围 + */ + private Integer t; + + /** + * 触发规则 + */ + private String triggerRule; + + /** + * 触发值 + */ + private Double triggerValue; + + /** + * 调整方向(1:正向,-1:反向) + */ + private Integer adjustDirection; + + /** + * 调整长度 + */ + private Integer adjustLength; + + /** + * 调整值计算规则 + */ + private String adjustValueRule; + + /** + * 是否启用(0禁用 1启用) + */ + private Integer isEnable; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 预测项名称 + */ + private String itemName; + + /** + * 预测项输出名称 + */ + private String outputName; +} \ No newline at end of file diff --git a/iailab-module-model/iailab-module-model-biz/src/main/resources/mapper/pre/MmPredictAutoAdjustConfigDao.xml b/iailab-module-model/iailab-module-model-biz/src/main/resources/mapper/pre/MmPredictAutoAdjustConfigDao.xml new file mode 100644 index 0000000..2f87064 --- /dev/null +++ b/iailab-module-model/iailab-module-model-biz/src/main/resources/mapper/pre/MmPredictAutoAdjustConfigDao.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > +<mapper namespace="com.iailab.module.model.mcs.pre.dao.MmPredictAutoAdjustConfigDao"> + <select id="getPage" resultType="com.iailab.module.model.mcs.pre.vo.MmPredictAutoAdjustConfigVO"> + select + t1.*, + t2.result_name output_name, + t3.itemname item_name + from t_mm_predict_auto_adjust_config t1 + left join t_mm_item_output t2 on t1.output_id = t2.id + left join t_mm_predict_item t3 on t2.itemid = t3.id + <where> + <if test="params.configName != null and params.configName != ''"> + and t1.config_name like CONCAT('%', #{params.configName},'%') + </if> + <if test="params.configCode != null and params.configCode != ''"> + and t1.config_code like CONCAT('%', #{params.configCode},'%') + </if> + </where> + order by t1.create_time desc + </select> +</mapper> \ No newline at end of file -- Gitblit v1.9.3