package com.zy.asrs.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.TypeReference;
|
import com.alibaba.fastjson.serializer.SerializerFeature;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.core.common.Cools;
|
import com.core.exception.CoolException;
|
import com.zy.asrs.domain.BasMapEditorDoc;
|
import com.zy.asrs.domain.BasMapEditorElement;
|
import com.zy.asrs.entity.BasDevp;
|
import com.zy.asrs.entity.BasMap;
|
import com.zy.asrs.entity.BasStation;
|
import com.zy.asrs.entity.DeviceConfig;
|
import com.zy.asrs.service.BasDevpService;
|
import com.zy.asrs.service.BasMapEditorService;
|
import com.zy.asrs.service.BasMapService;
|
import com.zy.asrs.service.BasStationService;
|
import com.zy.asrs.service.DeviceConfigService;
|
import com.zy.asrs.utils.MapExcelUtils;
|
import com.zy.common.utils.RedisUtil;
|
import com.zy.core.enums.RedisKeyType;
|
import com.zy.core.enums.SlaveType;
|
import com.zy.core.model.StationObjModel;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.io.IOException;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.Collections;
|
import java.util.Date;
|
import java.util.HashMap;
|
import java.util.HashSet;
|
import java.util.LinkedHashMap;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
import java.util.TreeSet;
|
|
@Service("basMapEditorService")
|
public class BasMapEditorServiceImpl implements BasMapEditorService {
|
|
private static final String FREE_EDITOR_MODE = "free-v1";
|
private static final Set<String> RUNTIME_TYPES = new HashSet<>(Arrays.asList(
|
"shelf", "crn", "dualCrn", "devp", "rgv"
|
));
|
private static final int DEFAULT_ROW_HEIGHT = 200;
|
private static final int DEFAULT_COL_WIDTH = 1000;
|
private static final double DEFAULT_CANVAS_WIDTH = 4200D;
|
private static final double DEFAULT_CANVAS_HEIGHT = 5200D;
|
private static final int X_SCALE = 40;
|
private static final int Y_SCALE = 8;
|
|
@Autowired
|
private BasMapService basMapService;
|
@Autowired
|
private BasDevpService basDevpService;
|
@Autowired
|
private DeviceConfigService deviceConfigService;
|
@Autowired
|
private BasStationService basStationService;
|
@Autowired
|
private MapExcelUtils mapExcelUtils;
|
@Autowired
|
private RedisUtil redisUtil;
|
|
@Override
|
public BasMapEditorDoc getEditorDoc(Integer lev) {
|
BasMap basMap = basMapService.getOne(new QueryWrapper<BasMap>().eq("lev", lev));
|
if (basMap == null) {
|
return null;
|
}
|
BasMapEditorDoc editorDoc = parseEditorDocJson(lev, basMap.getOriginData());
|
if (editorDoc != null) {
|
return editorDoc;
|
}
|
if (Cools.isEmpty(basMap.getData())) {
|
return null;
|
}
|
return toFreeEditorDoc(lev, parseStoredMapData(basMap.getData()));
|
}
|
|
@Override
|
public List<BasMapEditorDoc> importExcelToEditorDocs(String filePath) throws IOException {
|
HashMap<Integer, List<List<HashMap<String, Object>>>> rawDataMap = mapExcelUtils.readExcel(filePath);
|
List<Integer> levList = new ArrayList<>(rawDataMap.keySet());
|
Collections.sort(levList);
|
|
List<BasMapEditorDoc> result = new ArrayList<>();
|
for (Integer lev : levList) {
|
List<List<HashMap<String, Object>>> storedData = convertRawExcelData(rawDataMap.get(lev));
|
result.add(toFreeEditorDoc(lev, storedData));
|
}
|
return result;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void importExcelAndPersist(String filePath) throws IOException {
|
HashMap<Integer, List<List<HashMap<String, Object>>>> rawDataMap = mapExcelUtils.readExcel(filePath);
|
List<Integer> levList = new ArrayList<>(rawDataMap.keySet());
|
Collections.sort(levList);
|
for (Integer lev : levList) {
|
List<List<HashMap<String, Object>>> storedData = convertRawExcelData(rawDataMap.get(lev));
|
persistFloorMap(lev, storedData, toFreeEditorDoc(lev, storedData));
|
}
|
rebuildDeviceAndStationSync();
|
clearMapCaches();
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void saveEditorDoc(BasMapEditorDoc doc) {
|
BasMapEditorDoc normalizedDoc = normalizeFreeDoc(doc);
|
List<List<HashMap<String, Object>>> storedData = compileToStoredMapData(normalizedDoc);
|
persistFloorMap(normalizedDoc.getLev(), storedData, normalizedDoc);
|
rebuildDeviceAndStationSync();
|
clearMapCaches();
|
}
|
|
private void persistFloorMap(Integer lev,
|
List<List<HashMap<String, Object>>> storedData,
|
BasMapEditorDoc editorDoc) {
|
if (lev == null || lev <= 0) {
|
throw new CoolException("楼层不能为空");
|
}
|
String dataJson = JSON.toJSONString(storedData);
|
String editorJson = JSON.toJSONString(editorDoc);
|
BasMap basMap = basMapService.getOne(new QueryWrapper<BasMap>().eq("lev", lev));
|
Date now = new Date();
|
if (basMap == null) {
|
basMap = new BasMap();
|
basMap.setCreateTime(now);
|
} else {
|
basMap.setLastData(basMap.getData());
|
}
|
basMap.setLev(lev);
|
basMap.setData(dataJson);
|
basMap.setOriginData(editorJson);
|
basMap.setUpdateTime(now);
|
basMapService.saveOrUpdate(basMap);
|
}
|
|
private BasMapEditorDoc parseEditorDocJson(Integer lev, String json) {
|
if (Cools.isEmpty(json)) {
|
return null;
|
}
|
try {
|
JSONObject object = JSON.parseObject(json);
|
if (object == null || !FREE_EDITOR_MODE.equals(object.getString("editorMode"))) {
|
return null;
|
}
|
BasMapEditorDoc doc = JSON.toJavaObject(object, BasMapEditorDoc.class);
|
if (doc == null) {
|
return null;
|
}
|
if (doc.getLev() == null || doc.getLev() <= 0) {
|
doc.setLev(lev);
|
}
|
return normalizeFreeDoc(doc);
|
} catch (Exception ignore) {
|
return null;
|
}
|
}
|
|
private BasMapEditorDoc toFreeEditorDoc(Integer lev, List<List<HashMap<String, Object>>> storedData) {
|
BasMapEditorDoc doc = new BasMapEditorDoc();
|
doc.setLev(lev);
|
doc.setEditorMode(FREE_EDITOR_MODE);
|
doc.setElements(new ArrayList<>());
|
if (storedData == null) {
|
doc.setCanvasWidth(DEFAULT_CANVAS_WIDTH);
|
doc.setCanvasHeight(DEFAULT_CANVAS_HEIGHT);
|
return doc;
|
}
|
|
int rowCount = storedData.size();
|
int colCount = 0;
|
for (List<HashMap<String, Object>> row : storedData) {
|
if (row != null && row.size() > colCount) {
|
colCount = row.size();
|
}
|
}
|
|
List<Integer> rowHeights = new ArrayList<>();
|
for (int r = 0; r < rowCount; r++) {
|
rowHeights.add(extractRowHeight(storedData, r));
|
}
|
List<Integer> colWidths = new ArrayList<>();
|
for (int c = 0; c < colCount; c++) {
|
colWidths.add(extractColWidth(storedData, c));
|
}
|
|
int[] yOffsets = new int[rowCount];
|
int[] xOffsets = new int[colCount];
|
int totalHeight = 0;
|
for (int r = 0; r < rowCount; r++) {
|
yOffsets[r] = totalHeight;
|
totalHeight += rowHeights.get(r);
|
}
|
int totalWidth = 0;
|
for (int c = 0; c < colCount; c++) {
|
xOffsets[c] = totalWidth;
|
totalWidth += colWidths.get(c);
|
}
|
|
List<BasMapEditorElement> elements = new ArrayList<>();
|
for (int r = 0; r < rowCount; r++) {
|
List<HashMap<String, Object>> row = storedData.get(r);
|
if (row == null) {
|
continue;
|
}
|
for (int c = 0; c < row.size(); c++) {
|
HashMap<String, Object> cell = row.get(c);
|
String type = normalizeType(getString(cell, "type", "none"));
|
if (!RUNTIME_TYPES.contains(type)) {
|
continue;
|
}
|
int rowSpan = Math.max(1, toInt(cell == null ? null : cell.get("rowSpan"), 1));
|
int colSpan = Math.max(1, toInt(cell == null ? null : cell.get("colSpan"), 1));
|
int widthRaw = 0;
|
for (int cc = c; cc < Math.min(c + colSpan, colWidths.size()); cc++) {
|
widthRaw += colWidths.get(cc);
|
}
|
int heightRaw = 0;
|
for (int rr = r; rr < Math.min(r + rowSpan, rowHeights.size()); rr++) {
|
heightRaw += rowHeights.get(rr);
|
}
|
BasMapEditorElement element = new BasMapEditorElement();
|
element.setId("el_" + r + "_" + c);
|
element.setType(type);
|
element.setX(xOffsets[c] / (double) X_SCALE);
|
element.setY(yOffsets[r] / (double) Y_SCALE);
|
element.setWidth(widthRaw / (double) X_SCALE);
|
element.setHeight(heightRaw / (double) Y_SCALE);
|
element.setValue(stringifyValue(cell == null ? null : cell.get("value")));
|
elements.add(element);
|
}
|
}
|
|
doc.setElements(elements);
|
doc.setCanvasWidth(Math.max(DEFAULT_CANVAS_WIDTH, totalWidth / (double) X_SCALE + 120D));
|
doc.setCanvasHeight(Math.max(DEFAULT_CANVAS_HEIGHT, totalHeight / (double) Y_SCALE + 120D));
|
return normalizeFreeDoc(doc);
|
}
|
|
private BasMapEditorDoc normalizeFreeDoc(BasMapEditorDoc source) {
|
if (source == null || source.getLev() == null || source.getLev() <= 0) {
|
throw new CoolException("楼层不能为空");
|
}
|
BasMapEditorDoc doc = new BasMapEditorDoc();
|
doc.setLev(source.getLev());
|
doc.setEditorMode(FREE_EDITOR_MODE);
|
doc.setCanvasWidth(toPositiveCanvasValue(source.getCanvasWidth(), DEFAULT_CANVAS_WIDTH, "画布宽度"));
|
doc.setCanvasHeight(toPositiveCanvasValue(source.getCanvasHeight(), DEFAULT_CANVAS_HEIGHT, "画布高度"));
|
|
List<BasMapEditorElement> elements = new ArrayList<>();
|
List<BasMapEditorElement> sourceElements = source.getElements();
|
if (sourceElements != null) {
|
for (int i = 0; i < sourceElements.size(); i++) {
|
elements.add(normalizeElement(sourceElements.get(i), i + 1));
|
}
|
}
|
ensureNoRectOverlap(elements);
|
doc.setElements(elements);
|
return doc;
|
}
|
|
private BasMapEditorElement normalizeElement(BasMapEditorElement source, int index) {
|
BasMapEditorElement element = new BasMapEditorElement();
|
element.setId(Cools.isEmpty(source == null ? null : source.getId()) ? "el_" + index : source.getId().trim());
|
element.setType(normalizeType(source == null ? null : source.getType()));
|
if (!RUNTIME_TYPES.contains(element.getType())) {
|
throw new CoolException("存在不支持的节点类型: " + element.getType());
|
}
|
double x = toNonNegativeDouble(source == null ? null : source.getX(), "元素 X 坐标");
|
double y = toNonNegativeDouble(source == null ? null : source.getY(), "元素 Y 坐标");
|
double width = toPositiveCanvasValue(source == null ? null : source.getWidth(), 0D, "元素宽度");
|
double height = toPositiveCanvasValue(source == null ? null : source.getHeight(), 0D, "元素高度");
|
element.setX(x);
|
element.setY(y);
|
element.setWidth(width);
|
element.setHeight(height);
|
element.setValue(stringifyValue(source == null ? null : source.getValue()));
|
if ("devp".equals(element.getType())) {
|
validateDevpValue(element.getValue(), index);
|
}
|
return element;
|
}
|
|
private void ensureNoRectOverlap(List<BasMapEditorElement> elements) {
|
for (int i = 0; i < elements.size(); i++) {
|
BasMapEditorElement a = elements.get(i);
|
for (int j = i + 1; j < elements.size(); j++) {
|
BasMapEditorElement b = elements.get(j);
|
if (rectanglesOverlap(a, b)) {
|
throw new CoolException("元素存在重叠: " + a.getId() + " 与 " + b.getId());
|
}
|
}
|
}
|
}
|
|
private boolean rectanglesOverlap(BasMapEditorElement a, BasMapEditorElement b) {
|
int aLeft = toRawWidth(a.getX());
|
int aTop = toRawHeight(a.getY());
|
int aRight = toRawWidth(safeDouble(a.getX()) + safeDouble(a.getWidth()));
|
int aBottom = toRawHeight(safeDouble(a.getY()) + safeDouble(a.getHeight()));
|
int bLeft = toRawWidth(b.getX());
|
int bTop = toRawHeight(b.getY());
|
int bRight = toRawWidth(safeDouble(b.getX()) + safeDouble(b.getWidth()));
|
int bBottom = toRawHeight(safeDouble(b.getY()) + safeDouble(b.getHeight()));
|
return aLeft < bRight && aRight > bLeft && aTop < bBottom && aBottom > bTop;
|
}
|
|
private List<List<HashMap<String, Object>>> compileToStoredMapData(BasMapEditorDoc doc) {
|
List<BasMapEditorElement> elements = doc.getElements() == null ? new ArrayList<BasMapEditorElement>() : doc.getElements();
|
if (elements.isEmpty()) {
|
return buildEmptyStoredMapData(doc);
|
}
|
|
TreeSet<Integer> xBounds = new TreeSet<>();
|
TreeSet<Integer> yBounds = new TreeSet<>();
|
xBounds.add(0);
|
yBounds.add(0);
|
List<CompiledRect> rects = new ArrayList<>();
|
for (BasMapEditorElement element : elements) {
|
CompiledRect rect = toCompiledRect(element);
|
rects.add(rect);
|
xBounds.add(rect.left);
|
xBounds.add(rect.right);
|
yBounds.add(rect.top);
|
yBounds.add(rect.bottom);
|
}
|
|
List<Integer> xList = new ArrayList<>(xBounds);
|
List<Integer> yList = new ArrayList<>(yBounds);
|
if (xList.size() < 2 || yList.size() < 2) {
|
return buildEmptyStoredMapData(doc);
|
}
|
|
int rowCount = yList.size() - 1;
|
int colCount = xList.size() - 1;
|
String[][] occupancy = new String[rowCount][colCount];
|
Map<String, Integer> xIndexMap = buildBoundaryIndexMap(xList);
|
Map<String, Integer> yIndexMap = buildBoundaryIndexMap(yList);
|
|
List<List<HashMap<String, Object>>> stored = new ArrayList<>();
|
for (int r = 0; r < rowCount; r++) {
|
List<HashMap<String, Object>> row = new ArrayList<>();
|
int cellHeight = yList.get(r + 1) - yList.get(r);
|
for (int c = 0; c < colCount; c++) {
|
int cellWidth = xList.get(c + 1) - xList.get(c);
|
row.add(createStoredCell("none", "", cellWidth, cellHeight, 1, 1, ""));
|
}
|
stored.add(row);
|
}
|
|
for (CompiledRect rect : rects) {
|
Integer rowStart = yIndexMap.get(String.valueOf(rect.top));
|
Integer rowEndIndex = yIndexMap.get(String.valueOf(rect.bottom));
|
Integer colStart = xIndexMap.get(String.valueOf(rect.left));
|
Integer colEndIndex = xIndexMap.get(String.valueOf(rect.right));
|
if (rowStart == null || rowEndIndex == null || colStart == null || colEndIndex == null) {
|
throw new CoolException("地图编译失败: 元素边界无法映射到网格");
|
}
|
int rowSpan = rowEndIndex - rowStart;
|
int colSpan = colEndIndex - colStart;
|
if (rowSpan <= 0 || colSpan <= 0) {
|
throw new CoolException("地图编译失败: 元素尺寸无效 " + rect.id);
|
}
|
|
for (int r = rowStart; r < rowStart + rowSpan; r++) {
|
for (int c = colStart; c < colStart + colSpan; c++) {
|
if (occupancy[r][c] != null) {
|
throw new CoolException("地图编译失败: 元素重叠 " + rect.id + " 与 " + occupancy[r][c]);
|
}
|
occupancy[r][c] = rect.id;
|
}
|
}
|
|
stored.get(rowStart).set(colStart, createStoredCell(
|
rect.type,
|
rect.value,
|
xList.get(colStart + 1) - xList.get(colStart),
|
yList.get(rowStart + 1) - yList.get(rowStart),
|
rowSpan,
|
colSpan,
|
""
|
));
|
for (int r = rowStart; r < rowStart + rowSpan; r++) {
|
for (int c = colStart; c < colStart + colSpan; c++) {
|
if (r == rowStart && c == colStart) {
|
continue;
|
}
|
stored.get(r).set(c, createStoredCell(
|
"merge",
|
rect.value,
|
xList.get(c + 1) - xList.get(c),
|
yList.get(r + 1) - yList.get(r),
|
1,
|
1,
|
rect.type
|
));
|
}
|
}
|
}
|
return stored;
|
}
|
|
private List<List<HashMap<String, Object>>> buildEmptyStoredMapData(BasMapEditorDoc doc) {
|
int widthRaw = Math.max(DEFAULT_COL_WIDTH, toRawWidth(doc == null ? null : doc.getCanvasWidth()));
|
int heightRaw = Math.max(DEFAULT_ROW_HEIGHT, toRawHeight(doc == null ? null : doc.getCanvasHeight()));
|
List<List<HashMap<String, Object>>> stored = new ArrayList<>();
|
List<HashMap<String, Object>> row = new ArrayList<>();
|
row.add(createStoredCell("none", "", widthRaw, heightRaw, 1, 1, ""));
|
stored.add(row);
|
return stored;
|
}
|
|
private CompiledRect toCompiledRect(BasMapEditorElement element) {
|
CompiledRect rect = new CompiledRect();
|
rect.id = element.getId();
|
rect.type = element.getType();
|
rect.value = normalizeCellValue(element.getType(), element.getValue());
|
rect.left = toRawWidth(element.getX());
|
rect.top = toRawHeight(element.getY());
|
rect.right = toRawWidth(safeDouble(element.getX()) + safeDouble(element.getWidth()));
|
rect.bottom = toRawHeight(safeDouble(element.getY()) + safeDouble(element.getHeight()));
|
if (rect.right <= rect.left || rect.bottom <= rect.top) {
|
throw new CoolException("元素尺寸无效: " + element.getId());
|
}
|
return rect;
|
}
|
|
private Map<String, Integer> buildBoundaryIndexMap(List<Integer> bounds) {
|
Map<String, Integer> result = new LinkedHashMap<>();
|
for (int i = 0; i < bounds.size(); i++) {
|
result.put(String.valueOf(bounds.get(i)), i);
|
}
|
return result;
|
}
|
|
private HashMap<String, Object> createStoredCell(String type,
|
String value,
|
int cellWidth,
|
int cellHeight,
|
int rowSpan,
|
int colSpan,
|
String mergeType) {
|
HashMap<String, Object> cell = new HashMap<>();
|
cell.put("type", normalizeType(type));
|
cell.put("value", stringifyValue(value));
|
cell.put("cellWidth", Math.max(1, cellWidth));
|
cell.put("cellHeight", Math.max(1, cellHeight));
|
cell.put("rowSpan", Math.max(1, rowSpan));
|
cell.put("colSpan", Math.max(1, colSpan));
|
if ("merge".equals(type)) {
|
cell.put("mergeType", normalizeType(mergeType));
|
}
|
return cell;
|
}
|
|
private List<List<HashMap<String, Object>>> parseStoredMapData(String json) {
|
if (Cools.isEmpty(json)) {
|
return new ArrayList<>();
|
}
|
List<List<HashMap<String, Object>>> data = JSON.parseObject(json, new TypeReference<List<List<HashMap<String, Object>>>>() {});
|
return data == null ? new ArrayList<List<HashMap<String, Object>>>() : data;
|
}
|
|
private List<List<HashMap<String, Object>>> convertRawExcelData(List<List<HashMap<String, Object>>> rawData) {
|
List<List<HashMap<String, Object>>> result = new ArrayList<>();
|
if (rawData == null) {
|
return result;
|
}
|
for (List<HashMap<String, Object>> row : rawData) {
|
List<HashMap<String, Object>> rowResult = new ArrayList<>();
|
if (row != null) {
|
for (HashMap<String, Object> cell : row) {
|
rowResult.add(convertRawExcelCell(cell));
|
}
|
}
|
result.add(rowResult);
|
}
|
return result;
|
}
|
|
private HashMap<String, Object> convertRawExcelCell(HashMap<String, Object> rawCell) {
|
HashMap<String, Object> target = new HashMap<>();
|
String nodeType = getString(rawCell, "nodeType", "none");
|
String normalizedType;
|
if ("shelf".equals(nodeType)) {
|
normalizedType = "shelf";
|
} else if ("crn".equals(nodeType)) {
|
normalizedType = "crn";
|
} else if ("dualCrn".equals(nodeType) || "dualcrn".equals(nodeType)) {
|
normalizedType = "dualCrn";
|
} else if ("devp".equals(nodeType)) {
|
normalizedType = "devp";
|
} else if ("rgv".equals(nodeType)) {
|
normalizedType = "rgv";
|
} else if ("merge".equals(nodeType)) {
|
normalizedType = "merge";
|
} else {
|
normalizedType = "none";
|
}
|
target.put("type", normalizedType);
|
target.put("value", stringifyValue(rawCell == null ? null : rawCell.get("value")));
|
target.put("cellWidth", Math.max(1, toInt(rawCell == null ? null : rawCell.get("cellWidth"), DEFAULT_COL_WIDTH)));
|
target.put("cellHeight", Math.max(1, toInt(rawCell == null ? null : rawCell.get("cellHeight"), DEFAULT_ROW_HEIGHT)));
|
target.put("rowSpan", Math.max(1, toInt(rawCell == null ? null : rawCell.get("rowSpan"), 1)));
|
target.put("colSpan", Math.max(1, toInt(rawCell == null ? null : rawCell.get("colSpan"), 1)));
|
if ("merge".equals(normalizedType)) {
|
target.put("mergeType", normalizeType(getString(rawCell, "mergeType", "none")));
|
}
|
return target;
|
}
|
|
private int extractRowHeight(List<List<HashMap<String, Object>>> storedData, int rowIndex) {
|
List<HashMap<String, Object>> row = storedData.get(rowIndex);
|
if (row == null) {
|
return DEFAULT_ROW_HEIGHT;
|
}
|
for (HashMap<String, Object> cell : row) {
|
int height = toInt(cell == null ? null : cell.get("cellHeight"), 0);
|
if (height > 0) {
|
return height;
|
}
|
}
|
return DEFAULT_ROW_HEIGHT;
|
}
|
|
private int extractColWidth(List<List<HashMap<String, Object>>> storedData, int colIndex) {
|
for (List<HashMap<String, Object>> row : storedData) {
|
if (row == null || colIndex >= row.size()) {
|
continue;
|
}
|
HashMap<String, Object> cell = row.get(colIndex);
|
int width = toInt(cell == null ? null : cell.get("cellWidth"), 0);
|
if (width > 0) {
|
return width;
|
}
|
}
|
return DEFAULT_COL_WIDTH;
|
}
|
|
private void validateDevpValue(String value, int elementIndex) {
|
if (Cools.isEmpty(value)) {
|
throw new CoolException("输送线节点缺少配置: 元素#" + elementIndex);
|
}
|
JSONObject jsonObject;
|
try {
|
jsonObject = JSON.parseObject(value);
|
} catch (Exception ex) {
|
throw new CoolException("输送线节点配置不是合法JSON: 元素#" + elementIndex);
|
}
|
if (jsonObject == null || jsonObject.getInteger("stationId") == null || jsonObject.getInteger("deviceNo") == null) {
|
throw new CoolException("输送线节点必须包含stationId和deviceNo: 元素#" + elementIndex);
|
}
|
Integer isInStation = jsonObject.getInteger("isInStation");
|
Integer isBarcodeStation = jsonObject.getInteger("isBarcodeStation");
|
Integer barcodeIdx = jsonObject.getInteger("barcodeIdx");
|
Integer barcodeStation = jsonObject.getInteger("barcodeStation");
|
Integer barcodeStationDeviceNo = jsonObject.getInteger("barcodeStationDeviceNo");
|
Integer backStation = jsonObject.getInteger("backStation");
|
Integer backStationDeviceNo = jsonObject.getInteger("backStationDeviceNo");
|
|
if (isInStation != null && isInStation == 1) {
|
if (!isPositiveInteger(barcodeStation) || !isPositiveInteger(barcodeStationDeviceNo)) {
|
throw new CoolException("入站点必须包含barcodeStation和barcodeStationDeviceNo: 元素#" + elementIndex);
|
}
|
}
|
if (isBarcodeStation != null && isBarcodeStation == 1) {
|
if (!isPositiveInteger(barcodeIdx) || !isPositiveInteger(backStation) || !isPositiveInteger(backStationDeviceNo)) {
|
throw new CoolException("条码站必须包含barcodeIdx、backStation和backStationDeviceNo: 元素#" + elementIndex);
|
}
|
}
|
}
|
|
private boolean isPositiveInteger(Integer value) {
|
return value != null && value > 0;
|
}
|
|
private double toPositiveCanvasValue(Double rawValue, double defaultValue, String fieldLabel) {
|
double value = safeDouble(rawValue);
|
if (value <= 0) {
|
if (defaultValue > 0) {
|
return defaultValue;
|
}
|
throw new CoolException(fieldLabel + "必须大于0");
|
}
|
return value;
|
}
|
|
private double toNonNegativeDouble(Double rawValue, String fieldLabel) {
|
double value = safeDouble(rawValue);
|
if (value < 0) {
|
throw new CoolException(fieldLabel + "不能小于0");
|
}
|
return value;
|
}
|
|
private int toRawWidth(Double displayValue) {
|
return Math.max(0, (int) Math.round(safeDouble(displayValue) * X_SCALE));
|
}
|
|
private int toRawHeight(Double displayValue) {
|
return Math.max(0, (int) Math.round(safeDouble(displayValue) * Y_SCALE));
|
}
|
|
private double safeDouble(Double value) {
|
return value == null ? 0D : value;
|
}
|
|
private void rebuildDeviceAndStationSync() {
|
SyncContext context = new SyncContext();
|
List<BasMap> basMapList = basMapService.list(new QueryWrapper<BasMap>().orderByAsc("lev"));
|
for (BasMap basMap : basMapList) {
|
collectStationsFromMap(context, basMap.getLev(), parseStoredMapData(basMap.getData()));
|
}
|
|
basStationService.remove(new QueryWrapper<BasStation>());
|
syncBasDevp(context);
|
syncBasStation(context);
|
}
|
|
private void collectStationsFromMap(SyncContext context, Integer lev, List<List<HashMap<String, Object>>> storedData) {
|
if (storedData == null) {
|
return;
|
}
|
for (List<HashMap<String, Object>> row : storedData) {
|
if (row == null) {
|
continue;
|
}
|
for (HashMap<String, Object> cell : row) {
|
String type = normalizeType(getString(cell, "type", "none"));
|
if (!"devp".equals(type)) {
|
continue;
|
}
|
JSONObject value = parseJsonObject(stringifyValue(cell.get("value")));
|
if (value == null) {
|
continue;
|
}
|
Integer deviceNo = value.getInteger("deviceNo");
|
Integer stationId = value.getInteger("stationId");
|
if (deviceNo == null || stationId == null) {
|
continue;
|
}
|
|
StationObjModel stationObjModel = new StationObjModel();
|
stationObjModel.setDeviceNo(deviceNo);
|
stationObjModel.setStationId(stationId);
|
stationObjModel.setStationLev(lev);
|
addStationModel(context.deviceStationMap, context.deviceStationDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), stationObjModel);
|
context.stationRegistry.put(stationId, stationObjModel);
|
|
Integer isBarcodeStation = value.getInteger("isBarcodeStation");
|
if (isBarcodeStation != null && isBarcodeStation == 1) {
|
StationObjModel barcodeStationModel = new StationObjModel();
|
barcodeStationModel.setDeviceNo(deviceNo);
|
barcodeStationModel.setStationId(stationId);
|
barcodeStationModel.setBarcodeIdx(value.getInteger("barcodeIdx"));
|
if (value.getInteger("backStation") != null) {
|
StationObjModel backStation = new StationObjModel();
|
backStation.setDeviceNo(value.getInteger("backStationDeviceNo"));
|
backStation.setStationId(value.getInteger("backStation"));
|
barcodeStationModel.setBackStation(backStation);
|
}
|
addStationModel(context.barcodeStationMap, context.barcodeStationDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), barcodeStationModel);
|
}
|
|
Integer isInStation = value.getInteger("isInStation");
|
if (isInStation != null && isInStation == 1) {
|
StationObjModel inStationModel = new StationObjModel();
|
inStationModel.setDeviceNo(deviceNo);
|
inStationModel.setStationId(stationId);
|
StationObjModel barcodeStation = new StationObjModel();
|
barcodeStation.setDeviceNo(value.getInteger("barcodeStationDeviceNo"));
|
barcodeStation.setStationId(value.getInteger("barcodeStation"));
|
inStationModel.setBarcodeStation(barcodeStation);
|
addStationModel(context.inStationMap, context.inStationDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), inStationModel);
|
}
|
|
Integer isOutStation = value.getInteger("isOutStation");
|
if (isOutStation != null && isOutStation == 1) {
|
addStationModel(context.outStationMap, context.outStationDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), cloneStationModel(stationObjModel));
|
}
|
|
Integer runBlockReassign = value.getInteger("runBlockReassign");
|
if (runBlockReassign != null && runBlockReassign == 1) {
|
addStationModel(context.runBlockReassignStationMap, context.runBlockReassignDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), cloneStationModel(stationObjModel));
|
}
|
|
Integer isOutOrder = value.getInteger("isOutOrder");
|
if (isOutOrder != null && isOutOrder == 1) {
|
addStationModel(context.outOrderStationMap, context.outOrderDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), cloneStationModel(stationObjModel));
|
}
|
|
Integer isLiftTransfer = value.getInteger("isLiftTransfer");
|
if (isLiftTransfer != null && isLiftTransfer == 1) {
|
addStationModel(context.liftTransferStationMap, context.liftTransferDedup, deviceNo, buildStationKey(deviceNo, stationId, lev), cloneStationModel(stationObjModel));
|
}
|
}
|
}
|
}
|
|
private void syncBasDevp(SyncContext context) {
|
Map<Integer, BasDevp> existingMap = new LinkedHashMap<>();
|
List<BasDevp> existingList = basDevpService.list();
|
for (BasDevp basDevp : existingList) {
|
existingMap.put(basDevp.getDevpNo(), basDevp);
|
}
|
|
Set<Integer> allDeviceNos = new LinkedHashSet<>();
|
allDeviceNos.addAll(existingMap.keySet());
|
allDeviceNos.addAll(context.deviceStationMap.keySet());
|
Date now = new Date();
|
|
for (Integer deviceNo : allDeviceNos) {
|
BasDevp basDevp = existingMap.get(deviceNo);
|
if (basDevp == null) {
|
basDevp = new BasDevp();
|
basDevp.setDevpNo(deviceNo);
|
basDevp.setStatus(1);
|
basDevp.setCreateTime(now);
|
}
|
|
List<StationObjModel> stationList = context.deviceStationMap.get(deviceNo);
|
List<StationObjModel> barcodeStationList = context.barcodeStationMap.get(deviceNo);
|
List<StationObjModel> inStationList = context.inStationMap.get(deviceNo);
|
List<StationObjModel> outStationList = context.outStationMap.get(deviceNo);
|
List<StationObjModel> runBlockReassignStationList = context.runBlockReassignStationMap.get(deviceNo);
|
List<StationObjModel> outOrderStationList = context.outOrderStationMap.get(deviceNo);
|
List<StationObjModel> liftTransferStationList = context.liftTransferStationMap.get(deviceNo);
|
|
basDevp.setStationList(toJsonOrNull(stationList));
|
basDevp.setBarcodeStationList(toJsonOrNull(barcodeStationList));
|
basDevp.setInStationList(toJsonOrNull(inStationList));
|
basDevp.setOutStationList(toJsonOrNull(outStationList));
|
basDevp.setRunBlockReassignLocStationList(toJsonOrNull(runBlockReassignStationList));
|
basDevp.setIsOutOrderList(toJsonOrNull(outOrderStationList));
|
basDevp.setIsLiftTransferList(toJsonOrNull(liftTransferStationList));
|
basDevp.setUpdateTime(now);
|
basDevpService.saveOrUpdate(basDevp);
|
|
DeviceConfig deviceConfig = deviceConfigService.getOne(new QueryWrapper<DeviceConfig>()
|
.eq("device_no", deviceNo)
|
.eq("device_type", String.valueOf(SlaveType.Devp)));
|
if (deviceConfig != null) {
|
deviceConfig.setFakeInitStatus(toJsonOrNull(stationList));
|
deviceConfigService.updateById(deviceConfig);
|
}
|
}
|
}
|
|
private void syncBasStation(SyncContext context) {
|
Date now = new Date();
|
for (StationObjModel stationObjModel : context.stationRegistry.values()) {
|
BasStation basStation = new BasStation();
|
basStation.setStationId(stationObjModel.getStationId());
|
basStation.setDeviceNo(stationObjModel.getDeviceNo());
|
basStation.setStationLev(stationObjModel.getStationLev());
|
basStation.setCreateTime(now);
|
basStation.setStatus(1);
|
basStationService.save(basStation);
|
}
|
}
|
|
private void addStationModel(Map<Integer, List<StationObjModel>> targetMap,
|
Map<Integer, Set<String>> dedupMap,
|
Integer deviceNo,
|
String dedupKey,
|
StationObjModel stationObjModel) {
|
Set<String> set = dedupMap.get(deviceNo);
|
if (set == null) {
|
set = new LinkedHashSet<>();
|
dedupMap.put(deviceNo, set);
|
}
|
if (!set.add(dedupKey)) {
|
return;
|
}
|
List<StationObjModel> list = targetMap.get(deviceNo);
|
if (list == null) {
|
list = new ArrayList<>();
|
targetMap.put(deviceNo, list);
|
}
|
list.add(stationObjModel);
|
}
|
|
private StationObjModel cloneStationModel(StationObjModel source) {
|
StationObjModel clone = new StationObjModel();
|
clone.setDeviceNo(source.getDeviceNo());
|
clone.setStationId(source.getStationId());
|
clone.setStationLev(source.getStationLev());
|
clone.setBarcodeIdx(source.getBarcodeIdx());
|
clone.setDeviceBay(source.getDeviceBay());
|
clone.setDeviceLev(source.getDeviceLev());
|
clone.setDeviceRow(source.getDeviceRow());
|
clone.setDualCrnExecuteStation(source.getDualCrnExecuteStation());
|
clone.setBarcodeStation(source.getBarcodeStation());
|
clone.setBackStation(source.getBackStation());
|
return clone;
|
}
|
|
private String toJsonOrNull(List<StationObjModel> list) {
|
if (list == null || list.isEmpty()) {
|
return null;
|
}
|
return JSON.toJSONString(list, SerializerFeature.DisableCircularReferenceDetect);
|
}
|
|
private void clearMapCaches() {
|
redisUtil.del(RedisKeyType.LOC_MAP_BASE.key);
|
redisUtil.del(RedisKeyType.LOC_MAST_MAP_LIST.key);
|
}
|
|
private String normalizeType(String type) {
|
if (type == null) {
|
return "none";
|
}
|
String value = String.valueOf(type).trim();
|
if ("dualcrn".equalsIgnoreCase(value)) {
|
return "dualCrn";
|
}
|
if (Cools.isEmpty(value)) {
|
return "none";
|
}
|
return value;
|
}
|
|
private String normalizeCellValue(String type, String value) {
|
if ("none".equals(type)) {
|
return "";
|
}
|
return stringifyValue(value);
|
}
|
|
private String stringifyValue(Object value) {
|
if (value == null) {
|
return "";
|
}
|
if (value instanceof String) {
|
return (String) value;
|
}
|
if (value instanceof JSONObject || value instanceof Map || value instanceof List) {
|
return JSON.toJSONString(value);
|
}
|
return String.valueOf(value);
|
}
|
|
private String getString(Map<String, Object> map, String key, String defaultValue) {
|
if (map == null || !map.containsKey(key) || map.get(key) == null) {
|
return defaultValue;
|
}
|
String value = stringifyValue(map.get(key));
|
return Cools.isEmpty(value) ? defaultValue : value;
|
}
|
|
private int toInt(Object value, int defaultValue) {
|
if (value == null) {
|
return defaultValue;
|
}
|
if (value instanceof Number) {
|
return ((Number) value).intValue();
|
}
|
try {
|
return (int) Math.round(Double.parseDouble(String.valueOf(value)));
|
} catch (Exception ignore) {
|
return defaultValue;
|
}
|
}
|
|
private JSONObject parseJsonObject(String json) {
|
if (Cools.isEmpty(json)) {
|
return null;
|
}
|
try {
|
return JSON.parseObject(json);
|
} catch (Exception ignore) {
|
return null;
|
}
|
}
|
|
private String buildStationKey(Integer deviceNo, Integer stationId, Integer lev) {
|
return deviceNo + "_" + stationId + "_" + lev;
|
}
|
|
private static class CompiledRect {
|
private String id;
|
private String type;
|
private String value;
|
private int left;
|
private int top;
|
private int right;
|
private int bottom;
|
}
|
|
private static class SyncContext {
|
private final Map<Integer, List<StationObjModel>> deviceStationMap = new LinkedHashMap<>();
|
private final Map<Integer, List<StationObjModel>> barcodeStationMap = new LinkedHashMap<>();
|
private final Map<Integer, List<StationObjModel>> inStationMap = new LinkedHashMap<>();
|
private final Map<Integer, List<StationObjModel>> outStationMap = new LinkedHashMap<>();
|
private final Map<Integer, List<StationObjModel>> runBlockReassignStationMap = new LinkedHashMap<>();
|
private final Map<Integer, List<StationObjModel>> outOrderStationMap = new LinkedHashMap<>();
|
private final Map<Integer, List<StationObjModel>> liftTransferStationMap = new LinkedHashMap<>();
|
private final Map<Integer, StationObjModel> stationRegistry = new LinkedHashMap<>();
|
|
private final Map<Integer, Set<String>> deviceStationDedup = new LinkedHashMap<>();
|
private final Map<Integer, Set<String>> barcodeStationDedup = new LinkedHashMap<>();
|
private final Map<Integer, Set<String>> inStationDedup = new LinkedHashMap<>();
|
private final Map<Integer, Set<String>> outStationDedup = new LinkedHashMap<>();
|
private final Map<Integer, Set<String>> runBlockReassignDedup = new LinkedHashMap<>();
|
private final Map<Integer, Set<String>> outOrderDedup = new LinkedHashMap<>();
|
private final Map<Integer, Set<String>> liftTransferDedup = new LinkedHashMap<>();
|
}
|
}
|