Commit d19cb0f3 authored by duanruiming's avatar duanruiming

[add] 路口优化->添加异常路口手动优化方案下发

parent 155bec87
package net.wanji.opt.cache;
import lombok.extern.slf4j.Slf4j;
import net.wanji.common.enums.ControlModeEnum;
import net.wanji.common.enums.TurnConvertEnum;
import net.wanji.common.enums.WeekEnum;
import net.wanji.common.framework.Constants;
import net.wanji.common.utils.tool.DateUtil;
import net.wanji.opt.dto.*;
import net.wanji.opt.service.CrossSchedulesService;
import net.wanji.opt.service.CrossSchemeService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* @author duanruiming
* @date 2023/03/02 10:42
*/
@Component
@Slf4j
public class PhaseDirTurnCache implements CommandLineRunner {
private static Map<String, CrossPhaseDTO> phaseMap = new ConcurrentHashMap<>();
@Resource
CrossSchedulesService crossSchedulesService;
@Resource
CrossSchemeService crossSchemeService;
/**
* 统一获取缓存方法
*
* @return
*/
public Map<String, CrossPhaseDTO> getPhaseDirTurnMap() {
return phaseMap;
}
@Override
public void run(String... args) {
init();
}
/**
* 初始化相位方向转向缓存
*
* key: crossId-dir-turn value:CrossPhaseDTO
*/
private synchronized void init() {
try {
long start = System.currentTimeMillis();
// 获取已执行的调度详情信息
List<CrossSchedulesDTO> schedulesDTOList = crossSchedulesService.listCrossSchedulesDetails(Constants.SystemParam.NULL, Constants.SystemParam.YES);
// 获取当前运行时段信息
List<CrossSectionDTO> sectionInfos = listSection(schedulesDTOList);
// 获取方案详情信息
List<CrossSchemeDTO> schemeDTOList = crossSchemeService.listCrossSchemeDetails(Constants.SystemParam.NULL);
Map<Integer, CrossSchemeDTO> schemeMap = schemeDTOList.stream().collect(Collectors.toMap(CrossSchemeDTO::getId, e -> e));
String key;
String crossId;
Integer dir;
String[] turnArr;
Set<String> turnSet = new HashSet<>();
for (CrossSectionDTO section : sectionInfos) { // 运行时段列表
crossId = section.getCrossId();
CrossSchemeDTO scheme = schemeMap.get(section.getSchemeId()); // 方案信息
if (Objects.isNull(scheme.getPhaseInfos())) {
log.error("{}-路口, {}-方案,方案中相位信息不能为空", crossId, scheme.getId());
continue;
}
for (CrossPhaseDTO phase : scheme.getPhaseInfos()) { // 相位信息列表
if (Objects.isNull(phase.getLightsInfos())) {
log.error("{}-路口, {}-方案,{}-灯组,灯组中车道信息不能为空", crossId, scheme.getId(), phase.getId());
continue;
}
for (CrossLightsDTO lights : phase.getLightsInfos()) { // 灯组信息列表
dir = lights.getDir();
if (Objects.isNull(lights.getLaneInfos())) {
log.error("{}-路口, {}-方案,{}-灯组,灯组中车道信息不能为空", crossId, scheme.getId(), lights.getId());
throw new Exception("灯组中车道信息不能为空");
}
for (LaneInfoDTO lane : lights.getLaneInfos()) {
// 车道转向转换为转向类型,并去重
turnArr = TurnConvertEnum.getCodeByKey(lane.getTurn()).split(Constants.SystemParam.SEPARATOR_UNDER_LINE);
turnSet.addAll(Arrays.stream(turnArr).collect(Collectors.toSet()));
}
for (String turn : turnSet) {
key = crossId + Constants.SystemParam.SEPARATOR_UNDER_LINE + dir + Constants.SystemParam.SEPARATOR_UNDER_LINE + turn;
phase.setDirType(dir);
phase.setTurnType(turn);
phaseMap.put(key, phase);
}
turnSet.clear();
}
}
}
long end = System.currentTimeMillis();
log.info("加载区域基础信息到缓存耗时:{}s,条数据,size={}", (end - start), phaseMap.size());
} catch (Exception e) {
log.error("相位方向转向缓存初始化失败", e);
}
}
/**
* 获取当前运行时段信息
*
* @param schedulesDTOList 调度列表
* @return
*/
private List<CrossSectionDTO> listSection(List<CrossSchedulesDTO> schedulesDTOList) {
List<CrossSectionDTO> sectionInfos = new ArrayList<>();
LocalTime currentTime = LocalTime.parse(DateUtil.getTime());
int week = DateUtil.getWeek(new Date()) == Constants.SystemParam.ZERO ? WeekEnum.SUNDAY.getCode() : DateUtil.getWeek(new Date()); // 获取当前星期
for (CrossSchedulesDTO schedules : schedulesDTOList) {
if (Objects.equals(schedules.getWeek(), Constants.SystemParam.ZERO) &&
!DateUtil.getDate().equals(schedules.getSpecialDate())) { // 先判断是否为特殊日期,如果特殊日期与当前日期不相同,则跳过
continue;
} else if (schedules.getWeek() != week) {
continue;
}
// 时段信息列表
if (Objects.nonNull(schedules.getSectionInfos())) {
for (CrossSectionDTO section : schedules.getSectionInfos()) {
LocalTime startTime = LocalTime.parse(section.getStartTime());
LocalTime endTime = LocalTime.parse(section.getEndTime());
// 过滤非当前时段数据
if (currentTime.isBefore(startTime) || currentTime.isAfter(endTime)) {
continue;
}
// 过滤控制模式为“非定周期”的数据
if (!ControlModeEnum.FIXED_PERIOD.getCode().equals(section.getControlMode())) {
continue;
}
section.setPlanNo(schedules.getPlanNo());
sectionInfos.add(section);
}
}
}
return sectionInfos;
}
}
package net.wanji.opt.controller.signalcontrol;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import net.wanji.common.framework.rest.JsonViewObject;
import net.wanji.opt.service.signalcontrol.FeignProxyService;
import net.wanji.opt.vo.GreenwaveVO;
import net.wanji.opt.vo.SchemeOptSendVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
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;
import javax.ws.rs.core.MediaType;
/**
* @author duanruiming
* @date 2023/03/01 20:44
*/
@Api(value = "SignalCommandOptController", description = "手动优化数据下发服务")
@RequestMapping("/signalControl")
@RestController
public class SignalCommandOptController {
@Autowired
private FeignProxyService feignProxyService;
@ApiOperation(value = "手动优化方案下发", notes = "优化方案下发", response = JsonViewObject.class,
produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
@PostMapping(value = "/greenwaveList",
produces = MediaType.APPLICATION_JSON, consumes = MediaType.APPLICATION_JSON)
@ApiResponses({
@ApiResponse(code = 200, message = "OK", response = GreenwaveVO.class),
})
public JsonViewObject schemeOptSend(@RequestBody @Validated SchemeOptSendVO schemeOptSendVO) throws Exception{
JsonViewObject jsonViewObject = feignProxyService.schemeOptSend(schemeOptSendVO);
return jsonViewObject;
}
}
......@@ -21,6 +21,7 @@ public class CrossSchemeOptLogPO {
private String phaseNo;
private int phaseOrderId;
private int oriGreenTime;
private int optGreenTime;
private int optTime;
private String optType;
private String optReason;
......
package net.wanji.opt.service.impl;
import lombok.extern.slf4j.Slf4j;
import net.wanji.common.enums.ControlModeEnum;
import net.wanji.common.enums.CrossStatusEnum;
import net.wanji.common.enums.TurnConvertEnum;
import net.wanji.common.enums.WeekEnum;
import net.wanji.common.framework.Constants;
import net.wanji.common.framework.rest.JsonViewObject;
import net.wanji.common.utils.tool.DateUtil;
import net.wanji.databus.dao.entity.RidInfoEntity;
import net.wanji.databus.dao.mapper.RidInfoMapper;
import net.wanji.databus.entity.develop.servicedevelop.develop.StatusCodeEnum;
import net.wanji.feign.pojo.vo.SchemeSendVO;
import net.wanji.feign.pojo.vo.SignalStatusVO;
import net.wanji.feign.service.UtcFeignClients;
import net.wanji.opt.cache.PhaseDirTurnCache;
import net.wanji.opt.dao.mapper.CrossPhaseMapper;
import net.wanji.opt.dao.mapper.CrossSchemeMapper;
import net.wanji.opt.dao.mapper.CrossSchemeOptLogMapper;
import net.wanji.opt.dto.*;
import net.wanji.opt.dto.CrossDirInfoDTO;
import net.wanji.opt.dto.CrossPhaseDTO;
import net.wanji.opt.dto.CrossTurnDataRealtimeDTO;
import net.wanji.opt.dto.CrossTurnInfoDTO;
import net.wanji.opt.po.base.CrossPhasePO;
import net.wanji.opt.po.base.CrossSchemeOptLogPO;
import net.wanji.opt.po.base.CrossSchemePO;
......@@ -34,7 +34,6 @@ import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;
......@@ -62,6 +61,8 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
CrossSchemeOptLogMapper crossSchemeOptLogMapper;
@Resource
UtcFeignClients utcFeignClients;
@Resource
PhaseDirTurnCache phaseDirTurnCache;
@Value("${crossOptParam.maxVehheadDist}")
private Double maxVehheadDist;
......@@ -84,7 +85,7 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
try {
// 相位配时信息,key: 路口编号_方向类型_转向类型,value: 相位配时信息
Map<String, CrossPhaseDTO> phaseMap = listPhaseList();
Map<String, CrossPhaseDTO> phaseMap = phaseDirTurnCache.getPhaseDirTurnMap();
setTurnList(abnormalCrossList, phaseMap);
setTurnList(crossDataRealtimePOList, phaseMap);
......@@ -723,111 +724,6 @@ public class CrossOptimizeServiceImpl implements CrossOptimizeService {
return false;
}
/**
* 相位配时信息
* key:路口编号_方向类型_转向类型
* value:相位配时信息
*
* @return
*/
private Map<String, CrossPhaseDTO> listPhaseList() throws Exception {
Map<String, CrossPhaseDTO> phaseMap = new HashMap<>();
// 获取已执行的调度详情信息
List<CrossSchedulesDTO> schedulesDTOList = crossSchedulesService.listCrossSchedulesDetails(Constants.SystemParam.NULL, Constants.SystemParam.YES);
// 获取当前运行时段信息
List<CrossSectionDTO> sectionInfos = listSection(schedulesDTOList);
// 获取方案详情信息
List<CrossSchemeDTO> schemeDTOList = crossSchemeService.listCrossSchemeDetails(Constants.SystemParam.NULL);
Map<Integer, CrossSchemeDTO> schemeMap = schemeDTOList.stream().collect(Collectors.toMap(CrossSchemeDTO::getId, e -> e));
String key;
String crossId;
Integer dir;
String[] turnArr;
Set<String> turnSet = new HashSet<>();
for (CrossSectionDTO section : sectionInfos) { // 运行时段列表
crossId = section.getCrossId();
CrossSchemeDTO scheme = schemeMap.get(section.getSchemeId()); // 方案信息
if (Objects.isNull(scheme.getPhaseInfos())) {
log.error("{}-路口, {}-方案,方案中相位信息不能为空", crossId, scheme.getId());
continue;
}
for (CrossPhaseDTO phase : scheme.getPhaseInfos()) { // 相位信息列表
if (Objects.isNull(phase.getLightsInfos())) {
log.error("{}-路口, {}-方案,{}-灯组,灯组中车道信息不能为空", crossId, scheme.getId(), phase.getId());
continue;
}
for (CrossLightsDTO lights : phase.getLightsInfos()) { // 灯组信息列表
dir = lights.getDir();
if (Objects.isNull(lights.getLaneInfos())) {
log.error("{}-路口, {}-方案,{}-灯组,灯组中车道信息不能为空", crossId, scheme.getId(), lights.getId());
throw new Exception("灯组中车道信息不能为空");
}
for (LaneInfoDTO lane : lights.getLaneInfos()) {
// 车道转向转换为转向类型,并去重
turnArr = TurnConvertEnum.getCodeByKey(lane.getTurn()).split(Constants.SystemParam.SEPARATOR_UNDER_LINE);
turnSet.addAll(Arrays.stream(turnArr).collect(Collectors.toSet()));
}
for (String turn : turnSet) {
key = crossId + Constants.SystemParam.SEPARATOR_UNDER_LINE + dir + Constants.SystemParam.SEPARATOR_UNDER_LINE + turn;
phase.setDirType(dir);
phase.setTurnType(turn);
phaseMap.put(key, phase);
}
turnSet.clear();
}
}
}
return phaseMap;
}
/**
* 获取当前运行时段信息
*
* @param schedulesDTOList 调度列表
* @return
*/
private List<CrossSectionDTO> listSection(List<CrossSchedulesDTO> schedulesDTOList) throws Exception {
List<CrossSectionDTO> sectionInfos = new ArrayList<>();
LocalTime currentTime = LocalTime.parse(DateUtil.getTime());
int week = DateUtil.getWeek(new Date()) == Constants.SystemParam.ZERO ? WeekEnum.SUNDAY.getCode() : DateUtil.getWeek(new Date()); // 获取当前星期
for (CrossSchedulesDTO schedules : schedulesDTOList) {
if (Objects.equals(schedules.getWeek(), Constants.SystemParam.ZERO) &&
!DateUtil.getDate().equals(schedules.getSpecialDate())) { // 先判断是否为特殊日期,如果特殊日期与当前日期不相同,则跳过
continue;
} else if (schedules.getWeek() != week) {
continue;
}
// 时段信息列表
if (Objects.nonNull(schedules.getSectionInfos())) {
for (CrossSectionDTO section : schedules.getSectionInfos()) {
LocalTime startTime = LocalTime.parse(section.getStartTime());
LocalTime endTime = LocalTime.parse(section.getEndTime());
// 过滤非当前时段数据
if (currentTime.isBefore(startTime) || currentTime.isAfter(endTime)) {
continue;
}
// 过滤控制模式为“非定周期”的数据
if (!ControlModeEnum.FIXED_PERIOD.getCode().equals(section.getControlMode())) {
continue;
}
section.setPlanNo(schedules.getPlanNo());
sectionInfos.add(section);
}
}
}
return sectionInfos;
}
/**
* 获取所有信控路口转向实时数据
* key 路口编号
......
package net.wanji.opt.service.signalcontrol;
import net.wanji.common.framework.rest.JsonViewObject;
import net.wanji.opt.vo.SchemeOptSendVO;
/**
* @author duanruiming
* @date 2023/03/01 20:59
*/
public interface FeignProxyService {
JsonViewObject schemeOptSend(SchemeOptSendVO schemeOptSendVO) throws Exception;
}
package net.wanji.opt.service.signalcontrol.impl;
import lombok.extern.slf4j.Slf4j;
import net.wanji.common.framework.rest.JsonViewObject;
import net.wanji.feign.pojo.vo.SchemeSendVO;
import net.wanji.feign.service.UtcFeignClients;
import net.wanji.opt.cache.PhaseDirTurnCache;
import net.wanji.opt.dao.mapper.CrossSchemeOptLogMapper;
import net.wanji.opt.dto.CrossPhaseDTO;
import net.wanji.opt.po.base.CrossSchemeOptLogPO;
import net.wanji.opt.service.signalcontrol.FeignProxyService;
import net.wanji.opt.vo.SchemeOptSendVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
/**
* @author duanruiming
* @date 2023/03/02 9:11
*/
@Service
@Slf4j
public class FeignProxyServiceImpl implements FeignProxyService {
@Autowired
private UtcFeignClients utcFeignClients;
@Autowired
private CrossSchemeOptLogMapper crossSchemeOptLogMapper;
@Autowired
private PhaseDirTurnCache phaseDirTurnCache;
@Override
public JsonViewObject schemeOptSend(SchemeOptSendVO schemeOptSendVO) throws Exception {
SchemeSendVO schemeSendVO = new SchemeSendVO();
BeanUtils.copyProperties(schemeOptSendVO, schemeSendVO);
JsonViewObject jsonViewObject = utcFeignClients.schemeSend(schemeSendVO);
if (Objects.isNull(jsonViewObject) || jsonViewObject.getCode() != 200) {
log.error("实时监控路口优化方案下发远程调用异常!", jsonViewObject);
throw new Exception();
}
// 将优化记录插入方案优化记录表
List<CrossSchemeOptLogPO> crossSchemeOptLogPOS = getCrossSchemeOptLogPOList(schemeOptSendVO);
crossSchemeOptLogMapper.insertBatch(crossSchemeOptLogPOS);
return jsonViewObject.success("手动下发优化方案成功");
}
/**
* 获取方案优化数据,插入到数据库
*
* @param schemeOptSendVO
* @return
*/
private List<CrossSchemeOptLogPO> getCrossSchemeOptLogPOList(SchemeOptSendVO schemeOptSendVO) {
List<CrossSchemeOptLogPO> crossSchemeOptLogPOS = new ArrayList<>();
Map<String, CrossPhaseDTO> phaseDirTurnMap = phaseDirTurnCache.getPhaseDirTurnMap();
long dataBatchTimeLong = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
Map<String, Integer> phaseOffsetTimeMap = schemeOptSendVO.getPhaseOffsetTimeMap();
String crossId = schemeOptSendVO.getCrossCode();
List<SchemeSendVO.Pattern> patternList = schemeOptSendVO.getPatternList();
for (SchemeSendVO.Pattern pattern : patternList) {
String patternNo = pattern.getPatternNo();
String offset = pattern.getOffset();
List<SchemeSendVO.Pattern.Ring> rings = pattern.getRings();
for (SchemeSendVO.Pattern.Ring ring : rings) {
String ringNo = ring.getRingNo();
List<SchemeSendVO.Pattern.Ring.Phase> phaseList = ring.getPhaseList();
for (SchemeSendVO.Pattern.Ring.Phase phase : phaseList) {
String phaseNo = phase.getPhaseNo();
String greenTime = phase.getGreenTime();
Integer greenTimeOffset = phaseOffsetTimeMap.get(phaseNo);
for (Map.Entry<String, CrossPhaseDTO> entry : phaseDirTurnMap.entrySet()) {
CrossPhaseDTO crossPhaseDTO = entry.getValue();
if (StringUtils.equals(crossId, crossPhaseDTO.getCrossId()) && StringUtils.equals(phaseNo, crossPhaseDTO.getPhaseNo())) {
Integer dirType = crossPhaseDTO.getDirType();
String turnType = crossPhaseDTO.getTurnType();
CrossSchemeOptLogPO crossSchemeOptLogPO = new CrossSchemeOptLogPO();
crossSchemeOptLogPO.setCrossId(crossId);
crossSchemeOptLogPO.setSchemeNo(patternNo);
crossSchemeOptLogPO.setDirType(dirType);
crossSchemeOptLogPO.setTurnType(turnType);
crossSchemeOptLogPO.setOffset(Integer.valueOf(offset));
crossSchemeOptLogPO.setRingNo(Integer.valueOf(ringNo));
crossSchemeOptLogPO.setPhaseNo(phaseNo);
crossSchemeOptLogPO.setPhaseOrderId(Integer.valueOf(phase.getSort()));
crossSchemeOptLogPO.setOriGreenTime(Integer.valueOf(greenTime) - greenTimeOffset);
crossSchemeOptLogPO.setOptGreenTime(Integer.valueOf(greenTime));
crossSchemeOptLogPO.setOptTime(greenTimeOffset);
crossSchemeOptLogPO.setOptType(schemeOptSendVO.getOptType());
crossSchemeOptLogPO.setOptReason(schemeOptSendVO.getOptReason());
crossSchemeOptLogPO.setStartTime(new Date());
crossSchemeOptLogPO.setIsRelation(schemeOptSendVO.getRelationFlag());
crossSchemeOptLogPO.setRelationCrossId(schemeOptSendVO.getRelationCrossId());
crossSchemeOptLogPO.setOptResult("成功");
crossSchemeOptLogPO.setOptResultDesc("成功");
crossSchemeOptLogPO.setDataBatchTime((int) dataBatchTimeLong);
}
}
}
}
}
return crossSchemeOptLogPOS;
}
}
package net.wanji.opt.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import net.wanji.feign.pojo.vo.SchemeSendVO;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.Map;
/**
* @author duanruiming
* @date 2023/03/01 19:48
*/
@Data
@ApiModel(value = "SchemeOptSendVO", description = "方案优化下发实体")
public class SchemeOptSendVO extends SchemeSendVO {
@ApiModelProperty(value = "优化类型: 0-手动优化; 1-自动优化; 2-恢复; 3-绿波优化; 4-区域优化; 5-高峰方案下发")
@NotBlank(message = "优化类型不能为空")
private String optType;
@ApiModelProperty(value = "调整原因: 1-失衡; 2-溢出; 3-拥堵; 4-绿波优化; 5-高峰方案下发-失衡; 6-高峰方案下发-溢出; 7-高峰方案下发-拥堵; 8-活动预案; 90-事故单点优化")
private String optReason;
@ApiModelProperty(value = "是否被关联调整: 1-否; 0-是")
private String relationFlag;
@ApiModelProperty(value = "关联路口方向编号")
private String relationCrossId;
@ApiModelProperty(value = "相位号-相位调整时间map")
@NotEmpty
private Map<String, Integer> phaseOffsetTimeMap;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment