索引版本返回

parent 87592467
...@@ -973,6 +973,23 @@ public class ResourceGanttController { ...@@ -973,6 +973,23 @@ public class ResourceGanttController {
return R.ok(versionData); return R.ok(versionData);
} }
@PostMapping("/getSceneVersionWithIndexPreview")
@Operation(summary = "获取版本号、当前索引和甘特概览", description = "返回版本信息,并按资源甘特数据统计场景时间、工单、订单、设备和预览图")
public R<Map<String, Object>> getSceneVersionWithIndexPreview(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "previewWidth、previewHeight 为可选字段,用于控制返回 preview 图片尺寸。",
content = @io.swagger.v3.oas.annotations.media.Content(
mediaType = "application/json",
examples = @io.swagger.v3.oas.annotations.media.ExampleObject(
name = "获取版本和缩略图示例",
value = "{\n \"sceneId\": \"F397427A76754E33A5655C41A7D0BC4A\",\n \"previewWidth\": 896,\n \"previewHeight\": 20\n}"
)
)
)
@RequestBody Map<String, Object> params) {
String sceneId = ParamValidator.getString(params, "sceneId", "场景ID");
return R.ok(planResultService.getSceneVersionWithIndexPreview(sceneId, params));
}
@PostMapping("/revertVersion") @PostMapping("/revertVersion")
@Operation(summary = "撤销操作", description = "撤销操作", @Operation(summary = "撤销操作", description = "撤销操作",
...@@ -1092,4 +1109,4 @@ public class ResourceGanttController { ...@@ -1092,4 +1109,4 @@ public class ResourceGanttController {
} }
} }
\ No newline at end of file
...@@ -57,7 +57,10 @@ public class Machine { ...@@ -57,7 +57,10 @@ public class Machine {
* 设备编码 * 设备编码
*/ */
private String code; private String code;
/**
* 设备类型
*
*/
private String capacityTypeName; private String capacityTypeName;
/** /**
......
...@@ -24,6 +24,12 @@ import lombok.extern.slf4j.Slf4j; ...@@ -24,6 +24,12 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
...@@ -3714,5 +3720,311 @@ public class PlanResultService { ...@@ -3714,5 +3720,311 @@ public class PlanResultService {
public Map<String, Object> getSceneVersionWithIndexPreview(String sceneId, Map<String, Object> params) {
Map<String, Object> versionData = getSceneVersion(sceneId);
Chromosome schedule = _sceneService.loadChromosomeFromFile(sceneId);
if (schedule == null) {
throw new RuntimeException("未找到对应的场景文件");
}
SceneVersionPreviewData previewData = collectSceneVersionPreviewData(schedule);
int previewWidth = getPositiveIntegerParam(params, "previewWidth", 100);
int previewHeight = getPositiveIntegerParam(params, "previewHeight", calculateDefaultPreviewHeight(previewData.equipCount));
return buildSceneVersionPreview(versionData, previewData, previewWidth, previewHeight);
}
private Map<String, Object> buildSceneVersionPreview(Map<String, Object> versionData,
SceneVersionPreviewData previewData,
int previewWidth,
int previewHeight) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("index", versionData == null ? -1 : versionData.get("index"));
result.put("start", previewData.minStart == null ? null : previewData.minStart.toLocalDate().toString());
result.put("end", previewData.maxEnd == null ? null : previewData.maxEnd.toLocalDate().toString());
result.put("taskCount", previewData.taskCount);
result.put("planCount", previewData.planCount);
result.put("equipCount", previewData.equipCount);
result.put("taskMinTime", previewData.taskMinTime);
result.put("taskMaxTime", previewData.taskMaxTime);
result.put("taskAverageTime", previewData.taskAverageTime);
result.put("previewWidth", previewWidth);
result.put("previewHeight", previewHeight);
result.put("preview", buildGanttPreviewPng(previewData, previewWidth, previewHeight));
result.put("list", versionData == null ? new ArrayList<>() : versionData.get("list"));
return result;
}
private SceneVersionPreviewData collectSceneVersionPreviewData(Chromosome schedule) {
SceneVersionPreviewData previewData = new SceneVersionPreviewData();
if (schedule == null || schedule.getResult() == null || schedule.getResult().isEmpty()) {
return previewData;
}
List<GAScheduleResult> genes = schedule.getResult();
Set<Long> resultMachineIds = new HashSet<>();
for (GAScheduleResult gene : genes) {
if (gene != null) {
resultMachineIds.add(gene.getMachineId());
}
}
List<Machine> machines = schedule.getInitMachines();
if (machines == null || machines.isEmpty()) {
return previewData;
}
for (Machine machine : machines) {
if (machine == null || !resultMachineIds.contains(machine.getId())) {
continue;
}
previewData.machineIds.add(machine.getId());
previewData.genesByMachine.put(machine.getId(), new ArrayList<>());
}
previewData.equipCount = previewData.machineIds.size();
if (previewData.equipCount == 0) {
return previewData;
}
Long minStartOffset = null;
Long maxEndOffset = null;
long durationSum = 0L;
int durationCount = 0;
Set<String> planIds = new HashSet<>();
for (GAScheduleResult gene : genes) {
if (gene == null || !previewData.genesByMachine.containsKey(gene.getMachineId())) {
continue;
}
previewData.genesByMachine.get(gene.getMachineId()).add(gene);
previewData.taskCount++;
planIds.add(getSchedulePlanKey(gene));
long startOffset = gene.getStartTime();
long endOffset = gene.getEndTime();
minStartOffset = minStartOffset == null ? startOffset : Math.min(minStartOffset, startOffset);
maxEndOffset = maxEndOffset == null ? endOffset : Math.max(maxEndOffset, endOffset);
long duration = endOffset - startOffset;
if (duration >= 0L) {
previewData.taskMinTime = durationCount == 0 ? duration : Math.min(previewData.taskMinTime, duration);
previewData.taskMaxTime = durationCount == 0 ? duration : Math.max(previewData.taskMaxTime, duration);
durationSum += duration;
durationCount++;
}
}
for (List<GAScheduleResult> machineGenes : previewData.genesByMachine.values()) {
machineGenes.sort(Comparator.comparingInt(GAScheduleResult::getStartTime));
}
previewData.planCount = planIds.size();
previewData.taskAverageTime = durationCount == 0 ? 0L : Math.round(durationSum / (double) durationCount);
previewData.minStartOffset = minStartOffset;
previewData.maxEndOffset = maxEndOffset;
if (schedule.getBaseTime() != null && minStartOffset != null && maxEndOffset != null) {
previewData.minStart = schedule.getBaseTime().plusSeconds(minStartOffset);
previewData.maxEnd = schedule.getBaseTime().plusSeconds(maxEndOffset);
}
return previewData;
}
private int getPositiveIntegerParam(Map<String, Object> params, String key, int defaultValue) {
if (params == null || params.get(key) == null) {
return defaultValue;
}
try {
int value = Integer.parseInt(String.valueOf(params.get(key)));
return value > 0 ? value : defaultValue;
} catch (NumberFormatException ex) {
return defaultValue;
}
}
private int calculateDefaultPreviewHeight(int equipCount) {
return Math.max(160, Math.min(1200, equipCount * 6));
}
private String buildGanttPreviewPng(SceneVersionPreviewData previewData,
int imageWidth,
int imageHeight) {
if (previewData == null || previewData.machineIds.isEmpty() || previewData.minStartOffset == null
|| previewData.maxEndOffset == null || previewData.maxEndOffset <= previewData.minStartOffset) {
return "";
}
long totalSeconds = Math.max(1L, previewData.maxEndOffset - previewData.minStartOffset);
int equipCount = previewData.machineIds.size();
BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
Map<String, Color> colorCache = new HashMap<>();
try {
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
graphics.setColor(new Color(0xf6f8fb));
graphics.fillRect(0, 0, imageWidth, imageHeight);
for (int i = 0; i < previewData.machineIds.size(); i++) {
List<GAScheduleResult> machineGenes = previewData.genesByMachine.get(previewData.machineIds.get(i));
if (machineGenes == null || machineGenes.isEmpty()) {
continue;
}
double y = getPreviewRowY(i, equipCount, imageHeight);
double rowHeight = getPreviewRowHeight(i, equipCount, imageHeight, y);
for (GAScheduleResult gene : machineGenes) {
long duration = gene.getEndTime() - gene.getStartTime();
if (duration < 0L) {
continue;
}
long offset = Math.max(0L, gene.getStartTime() - previewData.minStartOffset);
double x = offset * (double) imageWidth / totalSeconds;
double width = Math.max(1D, Math.max(1L, duration) * (double) imageWidth / totalSeconds);
if (x >= imageWidth || x + width <= 0) {
continue;
}
if (x < 0) {
width += x;
x = 0;
}
width = Math.min(width, imageWidth - x);
if (width <= 0 || rowHeight <= 0) {
continue;
}
fillPreviewRect(graphics, x, y, width, rowHeight,
getScheduleTaskColor(gene, colorCache), imageWidth, imageHeight);
}
}
} finally {
graphics.dispose();
}
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
ImageIO.write(image, "png", output);
return "data:image/png;base64," + Base64.getEncoder().encodeToString(output.toByteArray());
} catch (IOException ex) {
log.warn("Failed to build Gantt preview PNG.", ex);
return "";
}
}
private void fillPreviewRect(Graphics2D graphics,
double x,
double y,
double width,
double height,
Color color,
int imageWidth,
int imageHeight) {
int left = Math.max(0, (int) Math.floor(x));
int top = Math.max(0, (int) Math.floor(y));
int right = Math.min(imageWidth, Math.max(left + 1, (int) Math.ceil(x + width)));
int bottom = Math.min(imageHeight, Math.max(top + 1, (int) Math.ceil(y + height)));
if (left >= imageWidth || top >= imageHeight || right <= left || bottom <= top) {
return;
}
graphics.setColor(color);
graphics.fillRect(left, top, right - left, bottom - top);
}
private double getPreviewRowY(int index, int equipCount, int imageHeight) {
if (equipCount <= 0 || imageHeight <= 0) {
return 0D;
}
if (imageHeight < equipCount) {
return Math.min(imageHeight - 1D, Math.floor(index * (double) imageHeight / equipCount));
}
return index * (double) imageHeight / equipCount;
}
private double getPreviewRowHeight(int index, int equipCount, int imageHeight, double y) {
if (equipCount <= 0 || imageHeight <= 0 || y >= imageHeight) {
return 0D;
}
if (imageHeight < equipCount) {
double nextY = Math.floor((index + 1D) * imageHeight / equipCount);
return Math.min(imageHeight - y, Math.max(1D, nextY - y));
}
double rowHeight = imageHeight / (double) equipCount;
return Math.min(rowHeight, Math.max(1D, rowHeight * 0.85D));
}
private Color getScheduleTaskColor(GAScheduleResult gene, Map<String, Color> colorCache) {
String key = getSchedulePlanKey(gene);
Color color = colorCache.get(key);
if (color != null) {
return color;
}
int hue = key == null ? 210 : (key.hashCode() & 0x7fffffff) % 360;
Color rgb = hslToRgb(hue, 0.70D, 0.55D);
color = new Color(rgb.getRed(), rgb.getGreen(), rgb.getBlue(), 199);
colorCache.put(key, color);
return color;
}
private String getSchedulePlanKey(GAScheduleResult gene) {
if (gene == null) {
return null;
}
if (gene.getOrderId() != null && !gene.getOrderId().trim().isEmpty()) {
return gene.getOrderId();
}
if (gene.getOrderCode() != null && !gene.getOrderCode().trim().isEmpty()) {
return gene.getOrderCode();
}
return String.valueOf(gene.getOperationId());
}
private Color hslToRgb(int hue, double saturation, double lightness) {
double c = (1D - Math.abs(2D * lightness - 1D)) * saturation;
double h = hue / 60D;
double x = c * (1D - Math.abs(h % 2D - 1D));
double r = 0D;
double g = 0D;
double b = 0D;
if (h < 1D) {
r = c;
g = x;
} else if (h < 2D) {
r = x;
g = c;
} else if (h < 3D) {
g = c;
b = x;
} else if (h < 4D) {
g = x;
b = c;
} else if (h < 5D) {
r = x;
b = c;
} else {
r = c;
b = x;
}
double m = lightness - c / 2D;
return new Color(toRgbChannel(r + m), toRgbChannel(g + m), toRgbChannel(b + m));
}
private int toRgbChannel(double value) {
return Math.max(0, Math.min(255, (int) Math.round(value * 255D)));
}
private static class SceneVersionPreviewData {
private final List<Long> machineIds = new ArrayList<>();
private final Map<Long, List<GAScheduleResult>> genesByMachine = new LinkedHashMap<>();
private LocalDateTime minStart;
private LocalDateTime maxEnd;
private Long minStartOffset;
private Long maxEndOffset;
private int taskCount;
private int planCount;
private int equipCount;
private long taskMinTime;
private long taskMaxTime;
private long taskAverageTime;
}
} }
...@@ -28,6 +28,8 @@ public class SceneService { ...@@ -28,6 +28,8 @@ public class SceneService {
@Autowired @Autowired
private RedisUtils redisUtils; private RedisUtils redisUtils;
private final ObjectMapper objectMapper = createObjectMapper();
private ObjectMapper createObjectMapper() { private ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule()); objectMapper.registerModule(new JavaTimeModule());
...@@ -72,7 +74,6 @@ public class SceneService { ...@@ -72,7 +74,6 @@ public class SceneService {
try { try {
long totalStart = System.nanoTime(); long totalStart = System.nanoTime();
ObjectMapper objectMapper = createObjectMapper();
SceneChromsome sceneChromsome = (SceneChromsome) redisUtils.get("SceneId." + sceneId); SceneChromsome sceneChromsome = (SceneChromsome) redisUtils.get("SceneId." + sceneId);
File file; File file;
if (sceneChromsome == null) { if (sceneChromsome == null) {
......
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