Commit 6a240d23 by 刘鑫

Merge branch 'develop' of https://gitlab.dituhui.com/bsh/project/project into develop

2 parents bb3c8ace 5d7a522a
......@@ -30,7 +30,7 @@ public interface DispatchOrderRepository extends CrudRepository<DispatchOrder, L
List<DispatchOrder> findAllWithoutConfirm(String groupId, String batchNo);
// 查看算法指派成功(也有抹掉技术员、时间情况),非confirm状态的
@Query("from DispatchOrder where teamId=?1 and batchNo=?2 and status !='CONFIRM' ")
@Query("from DispatchOrder where teamId=?1 and batchNo=?2 ")
List<DispatchOrder> findAllWithoutConfirm2(String teamId, String batchNo);
Optional<DispatchOrder> findByGroupIdAndBatchNoAndOrderIdAndDt(String groupId, String batchNo, String orderId, String dt);
......
......@@ -20,8 +20,11 @@ import lombok.Setter;
public class Customer {
private long id;
private String code;
private String status;
// 已分配
private String dispatchedTechnicianCode;
// 已排除
......@@ -56,7 +59,7 @@ public class Customer {
}
public Customer(long id, String code, String dt, Location location, int startTime, int endTime, String requiredSkill,
int serviceDuration) {
int serviceDuration, String status) {
this.id = id;
this.code = code;
this.dt = dt;
......@@ -65,6 +68,7 @@ public class Customer {
this.endTime = endTime;
this.requiredSkill = requiredSkill;
this.serviceDuration = serviceDuration;
this.status = status;
}
@InverseRelationShadowVariable(sourceVariableName = "customerList")
......
......@@ -26,7 +26,7 @@ public interface ExtractService {
/*
* 按小队将dispath_order 中的计算结果,回写到 order_info
* */
void extractDispatchToOrder2(String TeamId, String batchNo, boolean isConfirm) ;
void extractDispatchToOrder2(String TeamId, String batchNo, boolean cutOff) ;
}
......@@ -49,7 +49,7 @@ public class ExtractServiceImpl implements ExtractService {
@Autowired
OrderInfoRepository orderInfoRepo;
@Autowired
OrgTeamEngineerDao orgTeamEngineerDao;
......@@ -179,132 +179,135 @@ public class ExtractServiceImpl implements ExtractService {
log.info("算法结果更新到工单完成, groupId:{}, batchNo:{}", groupId, batchNo);
}
/**
* 将dispath_order 中的计算结果,回写到 order_info
* order_info(主要更新状态)
*/
@Transactional(isolation = READ_COMMITTED, propagation = Propagation.REQUIRED)
@Override
public void extractDispatchToOrder2(String teamId, String batchNo, boolean isConfirm) {
log.info("算法结果更新到工单, teamId:{}, batchNo:{}", teamId, batchNo);
entityManager.clear();
// 工程师数据准备
// Map<String, EngineerInfo> engineerInfoMap =
// engineerInfoRepo.findByGroupId(groupId).stream().collect(Collectors.toMap(EngineerInfo::getEngineerCode,
// y -> y));
final Map<String, EngineerInfo> engineerInfoMap = Maps.newHashMap();
List<OrgTeamEngineerEntity> engineers = orgTeamEngineerDao.findAllByTeamId(teamId);
if (CollectionUtils.isNotEmpty(engineers)) {
List<String> codes = engineers.stream().map(o -> o.getEngineerCode()).collect(Collectors.toList());
List<EngineerInfo> engineers2 = engineerInfoRepo.findByEngineerCodeIn(codes);
if (CollectionUtils.isNotEmpty(engineers2)) {
engineerInfoMap
.putAll(engineers2.stream().collect(Collectors.toMap(EngineerInfo::getEngineerCode, y -> y)));
}
}
List<DispatchOrder> dispatchOrderList = dispatchOrderRepo.findAllWithoutConfirm2(teamId, batchNo);
log.info("算法结果更新到工单, step1-开始处理, teamId:{}, batchNo:{}, order-size:{}", teamId, batchNo, dispatchOrderList.size());
AtomicInteger atomicInteger = new AtomicInteger();
dispatchOrderList.forEach(dispatchOrder -> {
int idx = atomicInteger.getAndIncrement();
String orderId = dispatchOrder.getOrderId();
String dt = dispatchOrder.getDt();
String engCode = dispatchOrder.getEngineerCode();
log.info("算法结果更新到工单, step1.1-loop, {}/{}, teamId:{}, batchNo:{}, orderId:{}, dt:{}, engCode:{}",
idx, dispatchOrderList.size(), teamId, batchNo, orderId, dt, engCode);
LocalDate localDt = LocalDate.parse(dt, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
Optional<OrderInfo> orderOpt = orderInfoRepo.findOrderInfoByOrderIdAndDt(orderId, localDt);
if (orderOpt.isEmpty()) {
log.warn("算法结果更新到工单, step1.1-loop, 工单不存在, teamId:{}, batchNo:{}, orderId:{}",
teamId, batchNo, orderId);
return;
}
OrderInfo orderInfo = orderOpt.get();
if (!("OPEN".equals(orderInfo.getBeanStatus()) &&
Set.of("AUTO_NOW", "AUTO_BATCH").contains(orderInfo.getAppointmentMethod()) &&
Set.of("INIT", "PRE").contains(orderInfo.getAppointmentStatus()) &&
"NORMAL".equals(orderInfo.getOrderStatus()) && "INIT".equals(orderInfo.getServiceStatus()))) {
log.warn("算法结果更新到工单, step1.1-loop, 工单状态异常, teamId:{}, batchNo:{}, orderId:{}, bean-status:{}, appointment-status:{}, order-status:{}, service-status:{}",
teamId, batchNo, orderId, orderInfo.getBeanStatus(), orderInfo.getAppointmentStatus(), orderInfo.getOrderStatus(), orderInfo.getServiceStatus());
return;
}
// 未分配技术员时,清理原分配的信息
if (!StrUtil.isEmpty(engCode)) {
EngineerInfo engineerInfo = engineerInfoMap.get(engCode);
if (engineerInfo == null) {
log.warn("算法结果更新到工单, step1.1-loop, 未找到技术员, teamId:{}, batchNo:{}, engCode:{}",
teamId, batchNo, engCode);
return;
}
String engName = engineerInfo.getName();
String phone = engineerInfo.getPhone();
public void extractDispatchToOrder2(String teamId, String batchNo, boolean cutOff) {
log.info("算法结果更新到工单, teamId:{}, batchNo:{}", teamId, batchNo);
entityManager.clear();
// 工程师数据准备
// Map<String, EngineerInfo> engineerInfoMap =
// engineerInfoRepo.findByGroupId(groupId).stream().collect(Collectors.toMap(EngineerInfo::getEngineerCode,
// y -> y));
final Map<String, EngineerInfo> engineerInfoMap = Maps.newHashMap();
List<OrgTeamEngineerEntity> engineers = orgTeamEngineerDao.findAllByTeamId(teamId);
if (CollectionUtils.isNotEmpty(engineers)) {
List<String> codes = engineers.stream().map(o -> o.getEngineerCode()).collect(Collectors.toList());
List<EngineerInfo> engineers2 = engineerInfoRepo.findByEngineerCodeIn(codes);
if (CollectionUtils.isNotEmpty(engineers2)) {
engineerInfoMap
.putAll(engineers2.stream().collect(Collectors.toMap(EngineerInfo::getEngineerCode, y -> y)));
}
}
List<DispatchOrder> dispatchOrderList = dispatchOrderRepo.findAllWithoutConfirm2(teamId, batchNo);
log.info("算法结果更新到工单, step1-开始处理, teamId:{}, batchNo:{}, order-size:{}", teamId, batchNo, dispatchOrderList.size());
AtomicInteger atomicInteger = new AtomicInteger();
dispatchOrderList.forEach(dispatchOrder -> {
int idx = atomicInteger.getAndIncrement();
String orderId = dispatchOrder.getOrderId();
String dt = dispatchOrder.getDt();
String engCode = dispatchOrder.getEngineerCode();
log.info("算法结果更新到工单, step1.1-loop, {}/{}, teamId:{}, batchNo:{}, orderId:{}, dt:{}, engCode:{}",
idx, dispatchOrderList.size(), teamId, batchNo, orderId, dt, engCode);
LocalDate localDt = LocalDate.parse(dt, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
Optional<OrderInfo> orderOpt = orderInfoRepo.findOrderInfoByOrderIdAndDt(orderId, localDt);
if (orderOpt.isEmpty()) {
log.warn("算法结果更新到工单, step1.1-loop, 工单不存在, teamId:{}, batchNo:{}, orderId:{}",
teamId, batchNo, orderId);
return;
}
OrderInfo orderInfo = orderOpt.get();
if (!("OPEN".equals(orderInfo.getBeanStatus())
&& Set.of("AUTO_NOW", "AUTO_BATCH", "MANUAL").contains(orderInfo.getAppointmentMethod())
&& Set.of("INIT", "PRE", "CONFIRM").contains(orderInfo.getAppointmentStatus())
&& "NORMAL".equals(orderInfo.getOrderStatus())
&& "INIT".equals(orderInfo.getServiceStatus()))) {
log.warn("算法结果更新到工单, step1.1-loop, 工单状态异常, teamId:{}, batchNo:{}, orderId:{}, bean-status:{}, appointment-status:{}, order-status:{}, service-status:{}",
teamId, batchNo, orderId, orderInfo.getBeanStatus(), orderInfo.getAppointmentStatus(), orderInfo.getOrderStatus(), orderInfo.getServiceStatus());
return;
}
// 未分配技术员时,清理原分配的信息
if (!StrUtil.isEmpty(engCode)) {
EngineerInfo engineerInfo = engineerInfoMap.get(engCode);
if (engineerInfo == null) {
log.warn("算法结果更新到工单, step1.1-loop, 未找到技术员, teamId:{}, batchNo:{}, engCode:{}",
teamId, batchNo, engCode);
return;
}
String engName = engineerInfo.getName();
String phone = engineerInfo.getPhone();
/*int age = 0;
if (!StringUtils.isNullOrEmpty(engineerInfo.getBirth())) {
DateTime birthDate = DateUtil.parse(engineerInfo.getBirth(), "yyyy-MM-dd");
age = DateUtil.age(birthDate.toJdkDate(), DateUtil.date());
}*/
orderInfo.setEngineerCode(engCode);
orderInfo.setEngineerName(engName);
orderInfo.setEngineerPhone(phone);
orderInfo.setPlanStartTime(dispatchOrder.getTimeBegin());
orderInfo.setPlanEndTime(dispatchOrder.getTimeEnd());
orderInfo.setArriveElapsed(dispatchOrder.getPathTime());
orderInfo.setArriveDistance(dispatchOrder.getPathDistance());
orderInfo.setAppointmentStatus(isConfirm ? "CONFIRM" : "PRE");
orderInfo.setDispatcher("AUTO_BATCH");
orderInfo.setUpdateTime(LocalDateTime.now());
orderInfoRepo.save(orderInfo);
if (isConfirm) {
OrderEvent orderEvent = new OrderEvent().setOrderId(orderId).setSuborderId(orderInfo.getSubId()).setHappen(LocalDateTime.now())
.setEvent("批量自动指派").setOperator("DISPATCH").setOperatorName("算法批量指派").setSource("PEA-DISPATCH")
.setDescription(String.format("批量自动指派:<%s,%s>", engCode, engName)).setMemo("")
.setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderEventRepo.save(orderEvent);
}
OrderLog orderLog = new OrderLog().setOrderId(orderId).setSuborderId(orderInfo.getSubId()).setSource("PEA-DISPATCH").setOperator("DISPATCH")
.setContent(String.format("批量自动指派:<%s,%s>", engCode, engName)).setContentOld("")
.setMemo("批量自动指派").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderLogRepo.save(orderLog);
} else {
log.warn("算法结果更新到工单, step1.2-loop, 未能分配到技术员, teamId:{}, batchNo:{}, orderId:{}, dt:{}",
teamId, batchNo, orderId, dt);
orderInfo.setEngineerCode("");
orderInfo.setEngineerName("");
orderInfo.setEngineerPhone("");
orderInfo.setPlanStartTime(null);
orderInfo.setPlanEndTime(null);
orderInfo.setArriveElapsed(0);
orderInfo.setArriveDistance(0);
orderInfo.setAppointmentStatus("INIT");
orderInfo.setDispatcher("AUTO_BATCH");
orderInfo.setUpdateTime(LocalDateTime.now());
orderInfoRepo.save(orderInfo);
OrderLog orderLog = new OrderLog().setOrderId(orderId).setSuborderId(orderInfo.getSubId()).setSource("PEA-DISPATCH").setOperator("DISPATCH")
.setContent(String.format("批量自动指派:<%s,%s>", "", "抹掉技术员")).setContentOld("")
.setMemo("批量自动指派").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderLogRepo.save(orderLog);
}
});
log.info("算法结果更新到工单完成, teamId:{}, batchNo:{}", teamId, batchNo);
orderInfo.setEngineerCode(engCode);
orderInfo.setEngineerName(engName);
orderInfo.setEngineerPhone(phone);
orderInfo.setPlanStartTime(dispatchOrder.getTimeBegin());
orderInfo.setPlanEndTime(dispatchOrder.getTimeEnd());
orderInfo.setArriveElapsed(dispatchOrder.getPathTime());
orderInfo.setArriveDistance(dispatchOrder.getPathDistance());
if (!"CONFIRM".equals(dispatchOrder.getStatus())) {
orderInfo.setAppointmentStatus(cutOff ? "CONFIRM" : "PRE");
}
orderInfo.setDispatcher("AUTO_BATCH");
orderInfo.setUpdateTime(LocalDateTime.now());
orderInfoRepo.save(orderInfo);
if (cutOff) {
OrderEvent orderEvent = new OrderEvent().setOrderId(orderId).setSuborderId(orderInfo.getSubId()).setHappen(LocalDateTime.now())
.setEvent("批量自动指派").setOperator("DISPATCH").setOperatorName("算法批量指派").setSource("PEA-DISPATCH")
.setDescription(String.format("批量自动指派:<%s,%s>", engCode, engName)).setMemo("")
.setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderEventRepo.save(orderEvent);
}
OrderLog orderLog = new OrderLog().setOrderId(orderId).setSuborderId(orderInfo.getSubId()).setSource("PEA-DISPATCH").setOperator("DISPATCH")
.setContent(String.format("批量自动指派:<%s,%s>", engCode, engName)).setContentOld("")
.setMemo("批量自动指派").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderLogRepo.save(orderLog);
} else {
log.warn("算法结果更新到工单, step1.2-loop, 未能分配到技术员, teamId:{}, batchNo:{}, orderId:{}, dt:{}",
teamId, batchNo, orderId, dt);
orderInfo.setEngineerCode("");
orderInfo.setEngineerName("");
orderInfo.setEngineerPhone("");
orderInfo.setPlanStartTime(null);
orderInfo.setPlanEndTime(null);
orderInfo.setArriveElapsed(0);
orderInfo.setArriveDistance(0);
orderInfo.setAppointmentStatus("INIT");
orderInfo.setDispatcher("AUTO_BATCH");
orderInfo.setUpdateTime(LocalDateTime.now());
orderInfoRepo.save(orderInfo);
OrderLog orderLog = new OrderLog().setOrderId(orderId).setSuborderId(orderInfo.getSubId()).setSource("PEA-DISPATCH").setOperator("DISPATCH")
.setContent(String.format("批量自动指派:<%s,%s>", "", "抹掉技术员")).setContentOld("")
.setMemo("批量自动指派").setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
orderLogRepo.save(orderLog);
}
});
log.info("算法结果更新到工单完成, teamId:{}, batchNo:{}", teamId, batchNo);
}
......
......@@ -259,7 +259,7 @@ public class SolveServiceImpl implements SolveService {
order.setPriority(0);
}
Customer customer = new Customer(order.getId(), order.getOrderId(), order.getDt(), location, start, end, order.getSkills(), order.getTakeTime());
Customer customer = new Customer(order.getId(), order.getOrderId(), order.getDt(), location, start, end, order.getSkills(), order.getTakeTime(), order.getStatus());
OrderInfo orderInfo = orderInfoRepository.findByOrderId(order.getOrderId()).get(0);
if ((StrUtil.equals("MANUAL", orderInfo.getAppointmentMethod())
&& StrUtil.equals("CONFIRM", orderInfo.getAppointmentStatus()))
......@@ -271,11 +271,11 @@ public class SolveServiceImpl implements SolveService {
customer.setExclusiveTechnicianCode(Objects.nonNull(orderInfo.getAppointEngineerCodes()) ?
orderInfo.getAppointEngineerCodes().trim() : null);
}
log.info(
"订单指定排除工程师, teamId:{}, batchNo:{}, orderId:{}, dispatchedTechnicianCode:{}, exclusiveTechnicianCode:{}",
teamId, batchNo, order.getOrderId(), customer.getDispatchedTechnicianCode(),
customer.getExclusiveTechnicianCode());
log.info(
"订单指定排除工程师, teamId:{}, batchNo:{}, orderId:{}, dispatchedTechnicianCode:{}, exclusiveTechnicianCode:{}",
teamId, batchNo, order.getOrderId(), customer.getDispatchedTechnicianCode(),
customer.getExclusiveTechnicianCode());
customerList.add(customer);
});
......@@ -284,62 +284,62 @@ public class SolveServiceImpl implements SolveService {
// technicianList
ArrayList<Technician> technicianList = new ArrayList<>();
dispatchEngineerRepo.findByTeamIdAndBatchNo(teamId, batchNo).forEach(engineer -> {
// 技术员时间窗
int[][] timeWindows = engineerCalendarService.timeWindows(engineer.getEngineerCode(), teamId,
LocalDateTimeUtil.parseDate(currDay, DateTimeFormatter.ofPattern("yyyy-MM-dd")));
if (null != timeWindows && timeWindows.length > 0) {
log.info("技术员时间窗, teamId:{}, batchNo:{}, engineerName:{}, timeWindows:{}", teamId, batchNo,
engineer.getEngineerName(), new Gson().toJson(timeWindows));
// 技术员技能
List<String> skillList = queryEngineerSkills(engineer.getEngineerCode());
// 距离偏好map
Location location = new Location(engineer.getId(), engineer.getEngineerCode(), "中心点",
StrUtil.isEmpty(engineer.getX()) ? 0 : Double.parseDouble(engineer.getX()),
StrUtil.isEmpty(engineer.getY()) ? 0 : Double.parseDouble(engineer.getY()));
Map<String, Long> preferedLoctionDistanceMap = new HashMap<String, Long>();
if (location.getLongitude() > 1 && location.getLatitude() > 1) {
// 设置了偏好位置
customerList.forEach(customer -> {
long distance = distanceCalculator.calculateDistance(location, customer.getLocation());
preferedLoctionDistanceMap.put(customer.getCode(), distance);
});
} else {
// 未设置,不参与计算
}
// 最大距离,硬约束:电动车固定40km上限,其他无限制
int maxDistance = engineer.getMaxDistance() * 1000;
if (engineer.getVehicleType() != null && engineer.getVehicleType() == 2) {
maxDistance = 40 * 1000;
}
// 技术员出发地
Double x = null;
Double y = null;
if (ObjectUtil.equal(engineer.getDeparture(), DepartureEnum.WORK_ADDR.getCode())) {
x = Double.parseDouble(engineer.getWorkX());
y = Double.parseDouble(engineer.getWorkY());
} else {
x = Double.parseDouble(orgWarehouseInfoEntity.getX());
y = Double.parseDouble(orgWarehouseInfoEntity.getY());
}
Location engineerLocation = new Location(engineer.getId(), engineer.getEngineerCode(), "起点", x, y);
Depot engineerDepot = new Depot(engineer.getId(), engineer.getEngineerCode(), engineerLocation, 60 * 8,
60 * 18);
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(),
engineer.getMaxMinute(), maxDistance, engineer.getVehicleType(), engineerDepot, timeWindows,
Set.copyOf(skillList), preferedLoctionDistanceMap);
technicianList.add(vehicle);
} else {
// 没有时间窗技术员,不参与派工
log.info("技术员时间窗, teamId:{}, batchNo:{}, engineerName:{}, no timeWindows", teamId, batchNo,
engineer.getEngineerName());
}
});
dispatchEngineerRepo.findByTeamIdAndBatchNo(teamId, batchNo).forEach(engineer -> {
// 技术员时间窗
int[][] timeWindows = engineerCalendarService.timeWindows(engineer.getEngineerCode(), teamId,
LocalDateTimeUtil.parseDate(currDay, DateTimeFormatter.ofPattern("yyyy-MM-dd")));
if (null != timeWindows && timeWindows.length > 0) {
log.info("技术员时间窗, teamId:{}, batchNo:{}, engineerName:{}, timeWindows:{}", teamId, batchNo,
engineer.getEngineerName(), new Gson().toJson(timeWindows));
// 技术员技能
List<String> skillList = queryEngineerSkills(engineer.getEngineerCode());
// 距离偏好map
Location location = new Location(engineer.getId(), engineer.getEngineerCode(), "中心点",
StrUtil.isEmpty(engineer.getX()) ? 0 : Double.parseDouble(engineer.getX()),
StrUtil.isEmpty(engineer.getY()) ? 0 : Double.parseDouble(engineer.getY()));
Map<String, Long> preferedLoctionDistanceMap = new HashMap<String, Long>();
if (location.getLongitude() > 1 && location.getLatitude() > 1) {
// 设置了偏好位置
customerList.forEach(customer -> {
long distance = distanceCalculator.calculateDistance(location, customer.getLocation());
preferedLoctionDistanceMap.put(customer.getCode(), distance);
});
} else {
// 未设置,不参与计算
}
// 最大距离,硬约束:电动车固定40km上限,其他无限制
int maxDistance = engineer.getMaxDistance() * 1000;
if (engineer.getVehicleType() != null && engineer.getVehicleType() == 2) {
maxDistance = 40 * 1000;
}
// 技术员出发地
Double x = null;
Double y = null;
if (ObjectUtil.equal(engineer.getDeparture(), DepartureEnum.WORK_ADDR.getCode())) {
x = Double.parseDouble(engineer.getWorkX());
y = Double.parseDouble(engineer.getWorkY());
} else {
x = Double.parseDouble(orgWarehouseInfoEntity.getX());
y = Double.parseDouble(orgWarehouseInfoEntity.getY());
}
Location engineerLocation = new Location(engineer.getId(), engineer.getEngineerCode(), "起点", x, y);
Depot engineerDepot = new Depot(engineer.getId(), engineer.getEngineerCode(), engineerLocation, 60 * 8,
60 * 18);
Technician vehicle = new Technician(engineer.getId(), engineer.getEngineerCode(), engineer.getMaxNum(),
engineer.getMaxMinute(), maxDistance, engineer.getVehicleType(), engineerDepot, timeWindows,
Set.copyOf(skillList), preferedLoctionDistanceMap);
technicianList.add(vehicle);
} else {
// 没有时间窗技术员,不参与派工
log.info("技术员时间窗, teamId:{}, batchNo:{}, engineerName:{}, no timeWindows", teamId, batchNo,
engineer.getEngineerName());
}
});
log.info("组织问题对象, technician-list, teamId:{}, batchNo:{}, technician-list:{}", teamId, batchNo, technicianList.size());
// locationlist 起点+订单地点
......@@ -535,7 +535,8 @@ public class SolveServiceImpl implements SolveService {
log.info("算法结果回写dispatch, step1-清除历史, groupId:{}, batchNo:{}", teamId, batchNo);
Object[] paramClear = {teamId, batchNo};
String sqlReset = "update dispatch_order set engineer_code='', seq=0, time_begin=null, time_end=null, path_time=0, path_distance=0 " + "where team_id=? and batch_no=? and status!='CONFIRM' ";
// confirm 状态工单不清除
String sqlReset = "update dispatch_order set engineer_code=null, seq=0, time_begin=null, time_end=null, path_time=0, path_distance=0 " + "where team_id=? and batch_no=? and status!='CONFIRM' ";
jdbcTemplate.update(sqlReset, paramClear);
log.info("算法结果回写dispatch, step2-开始回写, teamId:{}, batchNo:{}", teamId, batchNo);
......@@ -561,7 +562,7 @@ public class SolveServiceImpl implements SolveService {
int pathTime = customer.getPathTimeFromPreviousStandstill();
long pathDistance = customer.getDistanceFromPreviousStandstill();
String sql = "update dispatch_order set engineer_code=?, seq=?, time_begin=? ,time_end=?, path_time=?, path_distance=? " + " where team_id=? and batch_no=? and order_id=? and dt=? and status!='CONFIRM' ";
String sql = "update dispatch_order set engineer_code=?, seq=?, time_begin=? ,time_end=?, path_time=?, path_distance=? " + " where team_id=? and batch_no=? and order_id=? and dt=?";
Object[] param = {technician.getCode(), idx, arriveTime, leaveTime, pathTime, pathDistance, teamId, batchNo, customer.getCode(), customer.getDt()};
int rowUpdated = jdbcTemplate.update(sql, param);
log.info("算法结果回写dispatch, step3-逐个客户处理, order_id:{}, engineer_code:{}, seq: {}, begin:{}, end:{} ,rowUpdated:{}", customer.getCode(), technician.getCode(), seq, arriveTime, leaveTime, rowUpdated);
......
......@@ -24,318 +24,320 @@ import com.google.common.collect.Maps;
public class DataUtils {
/**
* 获取初始化测试数据
*
* @return
* @throws UncheckedIOException
* @throws FileNotFoundException
*/
public static DispatchSolution getInitialProblem() throws UncheckedIOException, FileNotFoundException {
Map<Integer, String> customerIndexMap = loadCustomerIndex();
Map<Integer, String> customerIndexXyMap = loadCustomerIndexXY();
Map<Integer, String> technicianIndexMap = loadTechnicianIndex();
Map<String, Set<String>> technicianCodeSkillsMap = loadTechnicianCodeSkillsMap();
Map<String, String> customerCodeSkillMap = loadCustomerCodeSkillMap();
// 偏好中心点位置
Map<String, String> technicianCodePreferredLocationMap = loadPreferredlocationMap();
Map<String, Map<String, Long>> preferredlocationDistanceMap = loadPreferredlocationDistanceMap(
technicianCodePreferredLocationMap);
Map<String, Integer> customerCodeServiceTimeMap = loadCustomerCodeServiceTimeMap();
DispatchSolution problem = createVehicleRoutingSolution(customerIndexMap, customerIndexXyMap,
technicianIndexMap, technicianCodeSkillsMap, customerCodeSkillMap, technicianCodePreferredLocationMap,
preferredlocationDistanceMap, customerCodeServiceTimeMap, false);
return problem;
}
/**
* 获取初始化测试数据
* fullDay 是否全天派工
* @return
* @throws UncheckedIOException
* @throws FileNotFoundException
*/
public static DispatchSolution getInitialProblem(boolean fullDay) throws UncheckedIOException, FileNotFoundException {
Map<Integer, String> customerIndexMap = loadCustomerIndex();
Map<Integer, String> customerIndexXyMap = loadCustomerIndexXY();
Map<Integer, String> technicianIndexMap = loadTechnicianIndex();
Map<String, Set<String>> technicianCodeSkillsMap = loadTechnicianCodeSkillsMap();
Map<String, String> customerCodeSkillMap = loadCustomerCodeSkillMap();
// 偏好中心点位置
Map<String, String> technicianCodePreferredLocationMap = loadPreferredlocationMap();
Map<String, Map<String, Long>> preferredlocationDistanceMap = loadPreferredlocationDistanceMap(
technicianCodePreferredLocationMap);
Map<String, Integer> customerCodeServiceTimeMap = loadCustomerCodeServiceTimeMap();
DispatchSolution problem = createVehicleRoutingSolution(customerIndexMap, customerIndexXyMap,
technicianIndexMap, technicianCodeSkillsMap, customerCodeSkillMap, technicianCodePreferredLocationMap,
preferredlocationDistanceMap, customerCodeServiceTimeMap,fullDay);
return problem;
}
private static Map<String, String> loadPreferredlocationMap() throws UncheckedIOException, FileNotFoundException {
List<String> technicianCodeLocation = IOUtils.readLines(new FileInputStream("data/technicianLocation.csv"),
"utf-8");
Map<String, String> technicianCodeLocationMap = new HashMap<String, String>();// 序号-code
for (int i = 0; i < technicianCodeLocation.size(); i++) {
String line = technicianCodeLocation.get(i);
String[] temps = line.split(",");
technicianCodeLocationMap.put(temps[0], temps[1] + "," + temps[2]);
}
return technicianCodeLocationMap;
}
private static Map<String, Integer> loadCustomerCodeServiceTimeMap()
throws UncheckedIOException, FileNotFoundException {
List<String> customerServiceTime = IOUtils.readLines(new FileInputStream("data/customerServiceTime.csv"),
"utf-8");
Map<String, Integer> customerCodeServiceTimeMap = new HashMap<String, Integer>();// code-time
for (int i = 0; i < customerServiceTime.size(); i++) {
String line = customerServiceTime.get(i);
String[] temps = line.split(",");
customerCodeServiceTimeMap.put(temps[0], Integer.parseInt(temps[1]));
}
return customerCodeServiceTimeMap;
}
private static Map<String, Map<String, Long>> loadPreferredlocationDistanceMap(
Map<String, String> technicianCodeLocationMap) throws UncheckedIOException, FileNotFoundException {
List<String> customerCodeLocation = IOUtils.readLines(new FileInputStream("data/customerLocation.csv"),
"utf-8");
Map<String, String> customerCodeLocationMap = new HashMap<String, String>();// 序号-code
for (int i = 0; i < customerCodeLocation.size(); i++) {
String line = customerCodeLocation.get(i);
String[] temps = line.split(",");
customerCodeLocationMap.put(temps[0], temps[1] + "," + temps[2]);
}
// 生成订单和技术员的偏好距离Map 技术员-订单-距离
Map<String, Map<String, Long>> customerTecnicianDistanceMap = new HashMap<String, Map<String, Long>>();
customerCodeLocationMap.forEach((customerCode, value) -> {
technicianCodeLocationMap.forEach((technicianCode, value2) -> {
String[] temps = RegExUtils.removeAll(value, "\"").split(",");
String[] temps2 = RegExUtils.removeAll(value2, "\"").split(",");
long distance = (long) getDistance(Double.parseDouble(temps[1]), Double.parseDouble(temps[0]),
Double.parseDouble(temps2[1]), Double.parseDouble(temps2[0]));
Map<String, Long> customerMaps = customerTecnicianDistanceMap.get(technicianCode);
if (null == customerMaps) {
customerMaps = new HashMap<String, Long>();
customerTecnicianDistanceMap.put(technicianCode, customerMaps);
}
customerMaps.put(customerCode, distance);
});
});
return customerTecnicianDistanceMap;
}
/**
* 获取经纬度距离
*
* @param lat1 y
* @param lon1 x
* @param lat2 y
* @param lon2 x
* @return
*/
private static double getDistance(double lat1, double lon1, double lat2, double lon2) {
double diffLongitudes = Math.toRadians(Math.abs(lon1 - lon2));
double diffLatitudes = Math.toRadians(Math.abs(lat1 - lat2));
double slat = Math.toRadians(lat1);
double flat = Math.toRadians(lat2);
// haversine formula
double a = Math.sin(diffLatitudes / 2) * Math.sin(diffLatitudes / 2)
+ Math.cos(slat) * Math.cos(flat) * Math.sin(diffLongitudes / 2) * Math.sin(diffLongitudes / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // angular distance in radians
return 6378137 * c;
}
private static Map<String, String> loadCustomerCodeSkillMap() throws UncheckedIOException, FileNotFoundException {
List<String> customerSkill = IOUtils.readLines(new FileInputStream("data/customerSkill.csv"), "utf-8");
Map<String, String> customerCodeSkillMap = new HashMap<String, String>();// code-技能
for (int i = 0; i < customerSkill.size(); i++) {
String line = customerSkill.get(i);
String[] temps = line.split(",");
customerCodeSkillMap.put(temps[0], temps[1]);
}
return customerCodeSkillMap;
}
private static Map<String, Set<String>> loadTechnicianCodeSkillsMap()
throws UncheckedIOException, FileNotFoundException {
List<String> technicianSkills = IOUtils.readLines(new FileInputStream("data/technicianSkills.csv"), "utf-8");
Map<String, Set<String>> technicianCodeSkillsMap = new HashMap<String, Set<String>>();// code-技能
for (int i = 0; i < technicianSkills.size(); i++) {
String line = technicianSkills.get(i);
String[] temps = line.split(",");
String code = temps[0];
Set<String> skills = technicianCodeSkillsMap.get(code);
if (null == skills) {
skills = new HashSet<>();
technicianCodeSkillsMap.put(code, skills);
}
skills.add(temps[1]);
}
return technicianCodeSkillsMap;
}
private static Map<Integer, String> loadTechnicianIndex() throws UncheckedIOException, FileNotFoundException {
List<String> technicianIndexlines = IOUtils.readLines(new FileInputStream("data/technicianIndex.csv"), "utf-8");
Map<Integer, String> technicianIndexMap = new HashMap<Integer, String>();// 序号-code
for (int i = 0; i < technicianIndexlines.size(); i++) {
technicianIndexMap.put(i + 1, technicianIndexlines.get(i));
}
return technicianIndexMap;
}
private static Map<Integer, String> loadCustomerIndex() throws UncheckedIOException, FileNotFoundException {
List<String> customerIndexlines = IOUtils.readLines(new FileInputStream("data/customerIndex.csv"), "utf-8");
Map<Integer, String> customerIndexMap = new HashMap<Integer, String>();// 序号-code
for (int i = 0; i < customerIndexlines.size(); i++) {
customerIndexMap.put(i + 1, customerIndexlines.get(i));
}
return customerIndexMap;
}
private static Map<Integer, String> loadCustomerIndexXY() throws UncheckedIOException, FileNotFoundException {
List<String> customerCodeXYlines = IOUtils.readLines(new FileInputStream("data/customerIndexXY.csv"), "utf-8");
Map<Integer, String> customerIndexXYMap = new HashMap<Integer, String>();// 序号-code
for (int i = 0; i < customerCodeXYlines.size(); i++) {
String line = customerCodeXYlines.get(i);
String[] temps = line.split(",");
customerIndexXYMap.put(i + 1,
RegExUtils.removeAll(temps[1], "\"") + "," + RegExUtils.removeAll(temps[2], "\""));
}
return customerIndexXYMap;
}
private static DispatchSolution createVehicleRoutingSolution(Map<Integer, String> customerIndexMap,
Map<Integer, String> customerIndexXyMap, Map<Integer, String> technicianIndexMap,
Map<String, Set<String>> technicianCodeSkillsMap, Map<String, String> customerCodeSkillMap,
Map<String, String> technicianCodePreferredLocationMap,
Map<String, Map<String, Long>> preferredlocationDistanceMap,
Map<String, Integer> customerCodeServiceTimeMap, boolean fullDay) throws UncheckedIOException, FileNotFoundException {
DispatchSolution vehicleRoutingSolution = new DispatchSolution();
// 翻转map
Map<String, Integer> customerIndexMap2 = customerIndexMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
// 初始化距离矩阵
List<String> pathMatrixlines = IOUtils.readLines(new FileInputStream("data/pathMatrix.csv"), "utf-8");
long[][] pathMatrix = new long[customerIndexMap.keySet().size() + 1][customerIndexMap.keySet().size() + 1];
for (int i = 0; i < pathMatrixlines.size(); i++) {
String line = pathMatrixlines.get(i);
String[] temps = line.split(",");
for (int j = 0; j < temps.length; j++) {
pathMatrix[i][j] = (long) (Float.parseFloat(temps[j]) * 1000);
}
}
Map<Integer, Location> locationIndex = new HashMap<Integer, Location>();
for (int i = 0; i < pathMatrix.length; i++) {
// 1 ~ N+1
String xyString = customerIndexXyMap.get(i + 1);
String[] temps = xyString.split(",");
locationIndex.put(i + 1, new Location(i + 1, Float.parseFloat(temps[0]), Float.parseFloat(temps[1])));
}
for (int i = 0; i < pathMatrix.length; i++) {
Location locationi = locationIndex.get(i + 1);
for (int j = 0; j < pathMatrix[i].length; j++) {
Location locationj = locationIndex.get(j + 1);
locationi.getDistanceMap(1).put(locationj, pathMatrix[i][j]);
}
}
// 初始化时间矩阵
List<String> pathTimeMatrixlines = IOUtils.readLines(new FileInputStream("data/pathTimeMatrix.csv"), "utf-8");
long[][] pathTimeMatrix = new long[customerIndexMap.keySet().size() + 1][customerIndexMap.keySet().size() + 1];
for (int i = 0; i < pathTimeMatrixlines.size(); i++) {
String line = pathTimeMatrixlines.get(i);
String[] temps = line.split(",");
for (int j = 0; j < temps.length; j++) {
// 秒转分钟
pathTimeMatrix[i][j] = (long) (Math.round(Float.parseFloat(temps[j]) ));
}
}
for (int i = 0; i < pathTimeMatrix.length; i++) {
Location locationi = locationIndex.get(i + 1);
for (int j = 0; j < pathTimeMatrix[i].length; j++) {
Location locationj = locationIndex.get(j + 1);
locationi.getDistanceTimeMap(1).put(locationj, pathTimeMatrix[i][j]);
}
}
// 初始化订单服务窗
List<String> customerWindowslines = IOUtils.readLines(new FileInputStream("data/customerWindows.csv"), "utf-8");
Map<Integer, Integer> customerStartMap = new HashMap<Integer, Integer>();
Map<Integer, Integer> customerEndMap = new HashMap<Integer, Integer>();
for (int i = 0; i < customerWindowslines.size(); i++) {
String line = customerWindowslines.get(i);
String[] temps = line.split(",");
customerStartMap.put(customerIndexMap2.get(temps[0]), 480 + Integer.parseInt(temps[1]));
customerEndMap.put(customerIndexMap2.get(temps[0]), 480 + Integer.parseInt(temps[2]));
}
// 初始化订单需要技能
Map<Integer, String> customerSkillMap = new HashMap<Integer, String>();
for (int i = 0; i < customerWindowslines.size(); i++) {
// 获取订单技能
customerSkillMap.put(i + 1, customerCodeSkillMap.get(customerIndexMap.get(i + 1)));
if (null == customerCodeSkillMap.get(customerIndexMap.get(i + 1))) {
System.err.printf("%s code:%s 没有技能 %n", i + 1, customerIndexMap.get(i + 1));
System.exit(0);
}
}
// 初始化订单+技能服务时间
List<Customer> customerList = new ArrayList<>();
for (int i = 0; i < customerIndexMap.keySet().size(); i++) {
customerList.add(new Customer(i + 1, customerIndexMap.get(i + 1), "2000-01-01", locationIndex.get(i + 2),
customerStartMap.get(i + 1), customerEndMap.get(i + 1), customerSkillMap.get(i + 1),
// 初始化技能服务时间
customerCodeServiceTimeMap.get(customerIndexMap.get(i + 1))));
}
// 初始化Depot
Depot depot = new Depot(1, locationIndex.get(1), 480, 1080);
// 初始化技术员
List<Technician> technicianList = new ArrayList<>();
for (int i = 0; i < technicianIndexMap.keySet().size(); i++) {
// 获取第i+1个技术员的技能set
Set<String> skills = technicianCodeSkillsMap.get(technicianIndexMap.get(i + 1));
if (null == skills || skills.size() == 0) {
System.err.printf("技术员%s code:%s 没有技能 %n", i + 1, technicianIndexMap.get(i + 1));
System.exit(0);
}
String xyString = technicianCodePreferredLocationMap.get(technicianIndexMap.get(i + 1));
String[] temps = xyString.split(",");
Location preferredlocation = new Location(i + 1, Float.parseFloat(RegExUtils.removeAll(temps[0], "\"")),
Float.parseFloat(RegExUtils.removeAll(temps[1], "\"")));
technicianList.add(new Technician(i + 1, technicianIndexMap.get(i + 1), depot, 480, fullDay ? 1440 : 1080 , skills,
preferredlocationDistanceMap.get(technicianIndexMap.get(i + 1)), preferredlocation));
}
// 已分配订单 测试数据,"订单code-技术员code"
Map<String, String> orderCodeTechnichanCodesMap = Maps.newHashMap();
orderCodeTechnichanCodesMap.put("106854117", "6200041211");
orderCodeTechnichanCodesMap.put("106854169", "6200040858");
orderCodeTechnichanCodesMap.put("106863823", "6200040858");
for (Customer customer : customerList) {
if (null != orderCodeTechnichanCodesMap.get(customer.getCode())) {
System.out.println("set code"+customer.getCode()+" : "+orderCodeTechnichanCodesMap.get(customer.getCode()));
customer.setDispatchedTechnicianCode(orderCodeTechnichanCodesMap.get(customer.getCode()));
}
}
vehicleRoutingSolution.setCustomerList(customerList);
vehicleRoutingSolution.setDepot(depot);
vehicleRoutingSolution.setLocationList(new ArrayList<>(locationIndex.values()));
vehicleRoutingSolution.setTechnicianList(technicianList);
return vehicleRoutingSolution;
}
/**
* 获取初始化测试数据
*
* @return
* @throws UncheckedIOException
* @throws FileNotFoundException
*/
public static DispatchSolution getInitialProblem() throws UncheckedIOException, FileNotFoundException {
Map<Integer, String> customerIndexMap = loadCustomerIndex();
Map<Integer, String> customerIndexXyMap = loadCustomerIndexXY();
Map<Integer, String> technicianIndexMap = loadTechnicianIndex();
Map<String, Set<String>> technicianCodeSkillsMap = loadTechnicianCodeSkillsMap();
Map<String, String> customerCodeSkillMap = loadCustomerCodeSkillMap();
// 偏好中心点位置
Map<String, String> technicianCodePreferredLocationMap = loadPreferredlocationMap();
Map<String, Map<String, Long>> preferredlocationDistanceMap = loadPreferredlocationDistanceMap(
technicianCodePreferredLocationMap);
Map<String, Integer> customerCodeServiceTimeMap = loadCustomerCodeServiceTimeMap();
DispatchSolution problem = createVehicleRoutingSolution(customerIndexMap, customerIndexXyMap,
technicianIndexMap, technicianCodeSkillsMap, customerCodeSkillMap, technicianCodePreferredLocationMap,
preferredlocationDistanceMap, customerCodeServiceTimeMap, false);
return problem;
}
/**
* 获取初始化测试数据
* fullDay 是否全天派工
*
* @return
* @throws UncheckedIOException
* @throws FileNotFoundException
*/
public static DispatchSolution getInitialProblem(boolean fullDay) throws UncheckedIOException, FileNotFoundException {
Map<Integer, String> customerIndexMap = loadCustomerIndex();
Map<Integer, String> customerIndexXyMap = loadCustomerIndexXY();
Map<Integer, String> technicianIndexMap = loadTechnicianIndex();
Map<String, Set<String>> technicianCodeSkillsMap = loadTechnicianCodeSkillsMap();
Map<String, String> customerCodeSkillMap = loadCustomerCodeSkillMap();
// 偏好中心点位置
Map<String, String> technicianCodePreferredLocationMap = loadPreferredlocationMap();
Map<String, Map<String, Long>> preferredlocationDistanceMap = loadPreferredlocationDistanceMap(
technicianCodePreferredLocationMap);
Map<String, Integer> customerCodeServiceTimeMap = loadCustomerCodeServiceTimeMap();
DispatchSolution problem = createVehicleRoutingSolution(customerIndexMap, customerIndexXyMap,
technicianIndexMap, technicianCodeSkillsMap, customerCodeSkillMap, technicianCodePreferredLocationMap,
preferredlocationDistanceMap, customerCodeServiceTimeMap, fullDay);
return problem;
}
private static Map<String, String> loadPreferredlocationMap() throws UncheckedIOException, FileNotFoundException {
List<String> technicianCodeLocation = IOUtils.readLines(new FileInputStream("data/technicianLocation.csv"),
"utf-8");
Map<String, String> technicianCodeLocationMap = new HashMap<String, String>();// 序号-code
for (int i = 0; i < technicianCodeLocation.size(); i++) {
String line = technicianCodeLocation.get(i);
String[] temps = line.split(",");
technicianCodeLocationMap.put(temps[0], temps[1] + "," + temps[2]);
}
return technicianCodeLocationMap;
}
private static Map<String, Integer> loadCustomerCodeServiceTimeMap()
throws UncheckedIOException, FileNotFoundException {
List<String> customerServiceTime = IOUtils.readLines(new FileInputStream("data/customerServiceTime.csv"),
"utf-8");
Map<String, Integer> customerCodeServiceTimeMap = new HashMap<String, Integer>();// code-time
for (int i = 0; i < customerServiceTime.size(); i++) {
String line = customerServiceTime.get(i);
String[] temps = line.split(",");
customerCodeServiceTimeMap.put(temps[0], Integer.parseInt(temps[1]));
}
return customerCodeServiceTimeMap;
}
private static Map<String, Map<String, Long>> loadPreferredlocationDistanceMap(
Map<String, String> technicianCodeLocationMap) throws UncheckedIOException, FileNotFoundException {
List<String> customerCodeLocation = IOUtils.readLines(new FileInputStream("data/customerLocation.csv"),
"utf-8");
Map<String, String> customerCodeLocationMap = new HashMap<String, String>();// 序号-code
for (int i = 0; i < customerCodeLocation.size(); i++) {
String line = customerCodeLocation.get(i);
String[] temps = line.split(",");
customerCodeLocationMap.put(temps[0], temps[1] + "," + temps[2]);
}
// 生成订单和技术员的偏好距离Map 技术员-订单-距离
Map<String, Map<String, Long>> customerTecnicianDistanceMap = new HashMap<String, Map<String, Long>>();
customerCodeLocationMap.forEach((customerCode, value) -> {
technicianCodeLocationMap.forEach((technicianCode, value2) -> {
String[] temps = RegExUtils.removeAll(value, "\"").split(",");
String[] temps2 = RegExUtils.removeAll(value2, "\"").split(",");
long distance = (long) getDistance(Double.parseDouble(temps[1]), Double.parseDouble(temps[0]),
Double.parseDouble(temps2[1]), Double.parseDouble(temps2[0]));
Map<String, Long> customerMaps = customerTecnicianDistanceMap.get(technicianCode);
if (null == customerMaps) {
customerMaps = new HashMap<String, Long>();
customerTecnicianDistanceMap.put(technicianCode, customerMaps);
}
customerMaps.put(customerCode, distance);
});
});
return customerTecnicianDistanceMap;
}
/**
* 获取经纬度距离
*
* @param lat1 y
* @param lon1 x
* @param lat2 y
* @param lon2 x
* @return
*/
private static double getDistance(double lat1, double lon1, double lat2, double lon2) {
double diffLongitudes = Math.toRadians(Math.abs(lon1 - lon2));
double diffLatitudes = Math.toRadians(Math.abs(lat1 - lat2));
double slat = Math.toRadians(lat1);
double flat = Math.toRadians(lat2);
// haversine formula
double a = Math.sin(diffLatitudes / 2) * Math.sin(diffLatitudes / 2)
+ Math.cos(slat) * Math.cos(flat) * Math.sin(diffLongitudes / 2) * Math.sin(diffLongitudes / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // angular distance in radians
return 6378137 * c;
}
private static Map<String, String> loadCustomerCodeSkillMap() throws UncheckedIOException, FileNotFoundException {
List<String> customerSkill = IOUtils.readLines(new FileInputStream("data/customerSkill.csv"), "utf-8");
Map<String, String> customerCodeSkillMap = new HashMap<String, String>();// code-技能
for (int i = 0; i < customerSkill.size(); i++) {
String line = customerSkill.get(i);
String[] temps = line.split(",");
customerCodeSkillMap.put(temps[0], temps[1]);
}
return customerCodeSkillMap;
}
private static Map<String, Set<String>> loadTechnicianCodeSkillsMap()
throws UncheckedIOException, FileNotFoundException {
List<String> technicianSkills = IOUtils.readLines(new FileInputStream("data/technicianSkills.csv"), "utf-8");
Map<String, Set<String>> technicianCodeSkillsMap = new HashMap<String, Set<String>>();// code-技能
for (int i = 0; i < technicianSkills.size(); i++) {
String line = technicianSkills.get(i);
String[] temps = line.split(",");
String code = temps[0];
Set<String> skills = technicianCodeSkillsMap.get(code);
if (null == skills) {
skills = new HashSet<>();
technicianCodeSkillsMap.put(code, skills);
}
skills.add(temps[1]);
}
return technicianCodeSkillsMap;
}
private static Map<Integer, String> loadTechnicianIndex() throws UncheckedIOException, FileNotFoundException {
List<String> technicianIndexlines = IOUtils.readLines(new FileInputStream("data/technicianIndex.csv"), "utf-8");
Map<Integer, String> technicianIndexMap = new HashMap<Integer, String>();// 序号-code
for (int i = 0; i < technicianIndexlines.size(); i++) {
technicianIndexMap.put(i + 1, technicianIndexlines.get(i));
}
return technicianIndexMap;
}
private static Map<Integer, String> loadCustomerIndex() throws UncheckedIOException, FileNotFoundException {
List<String> customerIndexlines = IOUtils.readLines(new FileInputStream("data/customerIndex.csv"), "utf-8");
Map<Integer, String> customerIndexMap = new HashMap<Integer, String>();// 序号-code
for (int i = 0; i < customerIndexlines.size(); i++) {
customerIndexMap.put(i + 1, customerIndexlines.get(i));
}
return customerIndexMap;
}
private static Map<Integer, String> loadCustomerIndexXY() throws UncheckedIOException, FileNotFoundException {
List<String> customerCodeXYlines = IOUtils.readLines(new FileInputStream("data/customerIndexXY.csv"), "utf-8");
Map<Integer, String> customerIndexXYMap = new HashMap<Integer, String>();// 序号-code
for (int i = 0; i < customerCodeXYlines.size(); i++) {
String line = customerCodeXYlines.get(i);
String[] temps = line.split(",");
customerIndexXYMap.put(i + 1,
RegExUtils.removeAll(temps[1], "\"") + "," + RegExUtils.removeAll(temps[2], "\""));
}
return customerIndexXYMap;
}
private static DispatchSolution createVehicleRoutingSolution(Map<Integer, String> customerIndexMap,
Map<Integer, String> customerIndexXyMap, Map<Integer, String> technicianIndexMap,
Map<String, Set<String>> technicianCodeSkillsMap, Map<String, String> customerCodeSkillMap,
Map<String, String> technicianCodePreferredLocationMap,
Map<String, Map<String, Long>> preferredlocationDistanceMap,
Map<String, Integer> customerCodeServiceTimeMap, boolean fullDay) throws UncheckedIOException, FileNotFoundException {
DispatchSolution vehicleRoutingSolution = new DispatchSolution();
// 翻转map
Map<String, Integer> customerIndexMap2 = customerIndexMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
// 初始化距离矩阵
List<String> pathMatrixlines = IOUtils.readLines(new FileInputStream("data/pathMatrix.csv"), "utf-8");
long[][] pathMatrix = new long[customerIndexMap.keySet().size() + 1][customerIndexMap.keySet().size() + 1];
for (int i = 0; i < pathMatrixlines.size(); i++) {
String line = pathMatrixlines.get(i);
String[] temps = line.split(",");
for (int j = 0; j < temps.length; j++) {
pathMatrix[i][j] = (long) (Float.parseFloat(temps[j]) * 1000);
}
}
Map<Integer, Location> locationIndex = new HashMap<Integer, Location>();
for (int i = 0; i < pathMatrix.length; i++) {
// 1 ~ N+1
String xyString = customerIndexXyMap.get(i + 1);
String[] temps = xyString.split(",");
locationIndex.put(i + 1, new Location(i + 1, Float.parseFloat(temps[0]), Float.parseFloat(temps[1])));
}
for (int i = 0; i < pathMatrix.length; i++) {
Location locationi = locationIndex.get(i + 1);
for (int j = 0; j < pathMatrix[i].length; j++) {
Location locationj = locationIndex.get(j + 1);
locationi.getDistanceMap(1).put(locationj, pathMatrix[i][j]);
}
}
// 初始化时间矩阵
List<String> pathTimeMatrixlines = IOUtils.readLines(new FileInputStream("data/pathTimeMatrix.csv"), "utf-8");
long[][] pathTimeMatrix = new long[customerIndexMap.keySet().size() + 1][customerIndexMap.keySet().size() + 1];
for (int i = 0; i < pathTimeMatrixlines.size(); i++) {
String line = pathTimeMatrixlines.get(i);
String[] temps = line.split(",");
for (int j = 0; j < temps.length; j++) {
// 秒转分钟
pathTimeMatrix[i][j] = (long) (Math.round(Float.parseFloat(temps[j])));
}
}
for (int i = 0; i < pathTimeMatrix.length; i++) {
Location locationi = locationIndex.get(i + 1);
for (int j = 0; j < pathTimeMatrix[i].length; j++) {
Location locationj = locationIndex.get(j + 1);
locationi.getDistanceTimeMap(1).put(locationj, pathTimeMatrix[i][j]);
}
}
// 初始化订单服务窗
List<String> customerWindowslines = IOUtils.readLines(new FileInputStream("data/customerWindows.csv"), "utf-8");
Map<Integer, Integer> customerStartMap = new HashMap<Integer, Integer>();
Map<Integer, Integer> customerEndMap = new HashMap<Integer, Integer>();
for (int i = 0; i < customerWindowslines.size(); i++) {
String line = customerWindowslines.get(i);
String[] temps = line.split(",");
customerStartMap.put(customerIndexMap2.get(temps[0]), 480 + Integer.parseInt(temps[1]));
customerEndMap.put(customerIndexMap2.get(temps[0]), 480 + Integer.parseInt(temps[2]));
}
// 初始化订单需要技能
Map<Integer, String> customerSkillMap = new HashMap<Integer, String>();
for (int i = 0; i < customerWindowslines.size(); i++) {
// 获取订单技能
customerSkillMap.put(i + 1, customerCodeSkillMap.get(customerIndexMap.get(i + 1)));
if (null == customerCodeSkillMap.get(customerIndexMap.get(i + 1))) {
System.err.printf("%s code:%s 没有技能 %n", i + 1, customerIndexMap.get(i + 1));
System.exit(0);
}
}
// 初始化订单+技能服务时间
List<Customer> customerList = new ArrayList<>();
for (int i = 0; i < customerIndexMap.keySet().size(); i++) {
customerList.add(new Customer(i + 1, customerIndexMap.get(i + 1), "2000-01-01", locationIndex.get(i + 2),
customerStartMap.get(i + 1), customerEndMap.get(i + 1), customerSkillMap.get(i + 1),
// 初始化技能服务时间
customerCodeServiceTimeMap.get(customerIndexMap.get(i + 1)),
null));
}
// 初始化Depot
Depot depot = new Depot(1, locationIndex.get(1), 480, 1080);
// 初始化技术员
List<Technician> technicianList = new ArrayList<>();
for (int i = 0; i < technicianIndexMap.keySet().size(); i++) {
// 获取第i+1个技术员的技能set
Set<String> skills = technicianCodeSkillsMap.get(technicianIndexMap.get(i + 1));
if (null == skills || skills.size() == 0) {
System.err.printf("技术员%s code:%s 没有技能 %n", i + 1, technicianIndexMap.get(i + 1));
System.exit(0);
}
String xyString = technicianCodePreferredLocationMap.get(technicianIndexMap.get(i + 1));
String[] temps = xyString.split(",");
Location preferredlocation = new Location(i + 1, Float.parseFloat(RegExUtils.removeAll(temps[0], "\"")),
Float.parseFloat(RegExUtils.removeAll(temps[1], "\"")));
technicianList.add(new Technician(i + 1, technicianIndexMap.get(i + 1), depot, 480, fullDay ? 1440 : 1080, skills,
preferredlocationDistanceMap.get(technicianIndexMap.get(i + 1)), preferredlocation));
}
// 已分配订单 测试数据,"订单code-技术员code"
Map<String, String> orderCodeTechnichanCodesMap = Maps.newHashMap();
orderCodeTechnichanCodesMap.put("106854117", "6200041211");
orderCodeTechnichanCodesMap.put("106854169", "6200040858");
orderCodeTechnichanCodesMap.put("106863823", "6200040858");
for (Customer customer : customerList) {
if (null != orderCodeTechnichanCodesMap.get(customer.getCode())) {
System.out.println("set code" + customer.getCode() + " : " + orderCodeTechnichanCodesMap.get(customer.getCode()));
customer.setDispatchedTechnicianCode(orderCodeTechnichanCodesMap.get(customer.getCode()));
}
}
vehicleRoutingSolution.setCustomerList(customerList);
vehicleRoutingSolution.setDepot(depot);
vehicleRoutingSolution.setLocationList(new ArrayList<>(locationIndex.values()));
vehicleRoutingSolution.setTechnicianList(technicianList);
return vehicleRoutingSolution;
}
}
......@@ -11,6 +11,7 @@ import java.util.stream.Collectors;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.optaplanner.constraint.streams.drools.DroolsConstraintStreamScoreDirector;
......@@ -400,7 +401,8 @@ public class DispatchSolutionUtils {
List<Customer> customerList = new ArrayList<>();
customerList.addAll(((Technician) indictedObject).getCustomerList());
for (Customer customer : customerList) {
if (!customer.isInTechnicianTimeWindows()) {
if (!customer.isInTechnicianTimeWindows()
&& !StrUtil.equals("CONFIRM", customer.getStatus())) {
// 更新shadow变量
updateShadowVariable(customer);
// 移除技术员
......@@ -411,6 +413,19 @@ public class DispatchSolutionUtils {
}
}
break;
case dispatchedMatch:
for (Object indictedObject : constraintMatch.getIndictedObjectList()) {
// 违反硬约束对象,根据具体约束返回不同类型对象
if (indictedObject instanceof Customer) {
Customer customer = (Customer) indictedObject;
// 更新shadow变量
updateShadowVariable(customer);
// 移除技术员
customer.getTechnician().getCustomerList().remove(customer);
solution.getUnDispatchedCustomers().add(customer);
}
}
break;
default:
break;
}
......
......@@ -3,7 +3,7 @@ server:
dispatch:
cron:
expr: 0 10 8-23 * * ?
expr: 0 9 8-23 * * ?
next-day-limit: 2
scheduler:
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!