package com.zy.core.network.fake;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONObject;
|
import com.core.common.SpringUtils;
|
import com.zy.asrs.entity.DeviceConfig;
|
import com.zy.common.utils.RedisUtil;
|
import com.zy.core.News;
|
import com.zy.core.enums.RedisKeyType;
|
import com.zy.core.enums.StationCommandType;
|
import com.zy.core.model.CommandResponse;
|
import com.zy.core.model.command.StationCommand;
|
import com.zy.core.network.api.ZyStationConnectApi;
|
import com.zy.core.network.entity.ZyStationStatusEntity;
|
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Random;
|
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.Executors;
|
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.locks.ReentrantLock;
|
|
public class ZyStationFakeSegConnect implements ZyStationConnectApi {
|
|
private static final long DEFAULT_FAKE_RUN_BLOCK_TIMEOUT_MS = 10000L;
|
private static final long WAIT_SEGMENT_TIMEOUT_MS = 30000L;
|
|
private static final String STATUS_WAITING = "WAITING";
|
private static final String STATUS_RUNNING = "RUNNING";
|
private static final String STATUS_BLOCKED = "BLOCKED";
|
private static final String STATUS_CANCELLED = "CANCELLED";
|
private static final String STATUS_TIMEOUT = "TIMEOUT";
|
private static final String STATUS_FINISHED = "FINISHED";
|
|
private final Map<Integer, ReentrantLock> stationLocks = new ConcurrentHashMap<Integer, ReentrantLock>();
|
private final Map<Integer, List<ZyStationStatusEntity>> deviceStatusMap = new ConcurrentHashMap<Integer, List<ZyStationStatusEntity>>();
|
private final Map<Integer, DeviceConfig> deviceConfigMap = new ConcurrentHashMap<Integer, DeviceConfig>();
|
private final Map<Integer, BlockingQueue<StationCommand>> taskQueues = new ConcurrentHashMap<Integer, BlockingQueue<StationCommand>>();
|
private final Map<Integer, Long> taskLastUpdateTime = new ConcurrentHashMap<Integer, Long>();
|
private final Map<Integer, Boolean> taskRunning = new ConcurrentHashMap<Integer, Boolean>();
|
private final ExecutorService executor = Executors.newCachedThreadPool();
|
|
private RedisUtil redisUtil;
|
|
public void addFakeConnect(DeviceConfig deviceConfig, RedisUtil redisUtil) {
|
this.redisUtil = redisUtil;
|
if (deviceConfigMap.containsKey(deviceConfig.getDeviceNo())) {
|
return;
|
}
|
deviceConfigMap.put(deviceConfig.getDeviceNo(), deviceConfig);
|
deviceStatusMap.put(deviceConfig.getDeviceNo(), new CopyOnWriteArrayList<ZyStationStatusEntity>());
|
}
|
|
@Override
|
public boolean connect() {
|
return true;
|
}
|
|
@Override
|
public boolean disconnect() {
|
executor.shutdownNow();
|
return true;
|
}
|
|
@Override
|
public List<ZyStationStatusEntity> getStatus(Integer deviceNo) {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
|
if (statusList == null) {
|
return new ArrayList<ZyStationStatusEntity>();
|
}
|
|
DeviceConfig deviceConfig = deviceConfigMap.get(deviceNo);
|
if (statusList.isEmpty() && deviceConfig != null) {
|
List<ZyStationStatusEntity> init = JSON.parseArray(deviceConfig.getFakeInitStatus(),
|
ZyStationStatusEntity.class);
|
if (init != null) {
|
statusList.addAll(init);
|
for (ZyStationStatusEntity status : statusList) {
|
status.setAutoing(true);
|
status.setLoading(false);
|
status.setInEnable(true);
|
status.setOutEnable(true);
|
status.setEmptyMk(false);
|
status.setFullPlt(false);
|
status.setRunBlock(false);
|
status.setPalletHeight(0);
|
status.setError(0);
|
status.setBarcode("");
|
}
|
}
|
}
|
|
return statusList;
|
}
|
|
@Override
|
public CommandResponse sendCommand(Integer deviceNo, StationCommand command) {
|
Integer taskNo = command.getTaskNo();
|
if (taskNo == null) {
|
return new CommandResponse(false, "任务号为空");
|
}
|
|
if (command.getCommandType() != StationCommandType.MOVE) {
|
handleCommand(deviceNo, command);
|
return new CommandResponse(true, "命令已受理(异步执行)");
|
}
|
|
if (isDirectMoveCommand(command)) {
|
handleDirectMoveCommand(deviceNo, command);
|
return new CommandResponse(true, "命令已受理(异步执行)");
|
}
|
|
taskQueues.computeIfAbsent(taskNo, key -> new LinkedBlockingQueue<StationCommand>()).offer(command);
|
taskLastUpdateTime.put(taskNo, System.currentTimeMillis());
|
|
if (taskRunning.putIfAbsent(taskNo, true) == null) {
|
executor.submit(new Runnable() {
|
@Override
|
public void run() {
|
runTaskLoop(deviceNo, taskNo);
|
}
|
});
|
}
|
|
return new CommandResponse(true, "命令已受理(异步执行)");
|
}
|
|
@Override
|
public CommandResponse sendOriginCommand(String address, short[] data) {
|
return new CommandResponse(true, "原始命令已受理(异步执行)");
|
}
|
|
@Override
|
public byte[] readOriginCommand(String address, int length) {
|
return new byte[0];
|
}
|
|
private boolean isDirectMoveCommand(StationCommand command) {
|
if (command == null || command.getCommandType() != StationCommandType.MOVE) {
|
return false;
|
}
|
List<Integer> path = command.getNavigatePath();
|
return (path == null || path.isEmpty()) && command.getStationId() != null
|
&& command.getStationId().equals(command.getTargetStaNo());
|
}
|
|
private void handleDirectMoveCommand(Integer deviceNo, StationCommand command) {
|
Integer taskNo = command.getTaskNo();
|
Integer stationId = command.getStationId();
|
Integer targetStationId = command.getTargetStaNo();
|
if (taskNo != null && taskNo > 0 && taskNo != 9999 && taskNo != 9998 && stationId != null
|
&& stationId.equals(targetStationId)) {
|
generateStationData(deviceNo, taskNo, stationId, targetStationId);
|
}
|
|
TaskRuntimeContext context = new TaskRuntimeContext(taskNo, getThreadImpl(deviceNo));
|
context.startStationId = stationId;
|
context.currentStationId = stationId;
|
context.finalTargetStationId = targetStationId;
|
context.initialized = true;
|
context.status = STATUS_RUNNING;
|
context.appendStitchedPath(Arrays.asList(stationId));
|
context.addPassedStation(stationId);
|
context.generateBarcode = checkTaskNoInArea(taskNo);
|
|
traceEvent(deviceNo, context, "MOVE_INIT", "同站点任务直接到位", buildDetails("stationId", stationId), false);
|
if (context.generateBarcode) {
|
generateStationBarcode(taskNo, stationId, deviceNo);
|
}
|
traceEvent(deviceNo, context, "ARRIVED", "任务已在起点站点完成", buildDetails("barcodeGenerated",
|
context.generateBarcode, "stationId", stationId), false);
|
context.status = STATUS_FINISHED;
|
traceEvent(deviceNo, context, "TASK_END", "任务执行完成", buildDetails("reason", STATUS_FINISHED), true);
|
}
|
|
private void runTaskLoop(Integer deviceNo, Integer taskNo) {
|
TaskRuntimeContext context = new TaskRuntimeContext(taskNo, getThreadImpl(deviceNo));
|
try {
|
while (true) {
|
if (Thread.currentThread().isInterrupted()) {
|
if (!isTerminalStatus(context.status)) {
|
context.status = STATUS_CANCELLED;
|
}
|
break;
|
}
|
|
if (hasTaskReset(taskNo)) {
|
context.status = STATUS_CANCELLED;
|
break;
|
}
|
|
BlockingQueue<StationCommand> commandQueue = taskQueues.get(taskNo);
|
if (commandQueue == null) {
|
break;
|
}
|
|
StationCommand command = commandQueue.poll(100, TimeUnit.MILLISECONDS);
|
if (command != null) {
|
taskLastUpdateTime.put(taskNo, System.currentTimeMillis());
|
context.lastCommandAt = System.currentTimeMillis();
|
handleIncomingSegment(deviceNo, context, command);
|
}
|
|
if (!context.pendingPathQueue.isEmpty()) {
|
if (!context.initialized || context.currentStationId == null) {
|
initializeTaskPosition(deviceNo, context);
|
continue;
|
}
|
|
if (!executeNextMove(deviceNo, context)) {
|
break;
|
}
|
continue;
|
}
|
|
if (handleIdleState(deviceNo, context)) {
|
break;
|
}
|
}
|
} catch (InterruptedException e) {
|
Thread.currentThread().interrupt();
|
if (!isTerminalStatus(context.status)) {
|
context.status = STATUS_CANCELLED;
|
}
|
} finally {
|
taskQueues.remove(taskNo);
|
taskLastUpdateTime.remove(taskNo);
|
taskRunning.remove(taskNo);
|
|
if (!isTerminalStatus(context.status)) {
|
context.status = STATUS_FINISHED;
|
}
|
traceEvent(deviceNo, context, "TASK_END", "任务执行结束并清理资源",
|
buildDetails("reason", context.status), true);
|
News.info("[WCS Debug] 任务{}执行结束并清理资源,状态={}", taskNo, context.status);
|
}
|
}
|
|
private void handleIncomingSegment(Integer deviceNo, TaskRuntimeContext context, StationCommand command) {
|
List<Integer> newPath = normalizePath(command.getNavigatePath());
|
Integer lastInQueue = getLastInQueue(context.pendingPathQueue);
|
int startIndex = getPathAppendStartIndex(newPath, context.currentStationId, lastInQueue);
|
|
context.setStartStationIdIfAbsent(command.getStationId());
|
if (!context.generateBarcode && checkTaskNoInArea(context.taskNo)) {
|
context.generateBarcode = true;
|
}
|
|
traceEvent(deviceNo, context, "SEGMENT_RECEIVED", "收到新的路径分段命令",
|
buildDetails("segmentPath", newPath, "appendStartIndex", startIndex, "currentStationId",
|
context.currentStationId, "queueTailStationId", lastInQueue, "commandStationId",
|
command.getStationId(), "commandTargetStationId", command.getTargetStaNo()),
|
false);
|
|
Integer commandTargetStationId = command.getTargetStaNo();
|
if (commandTargetStationId != null) {
|
if (!commandTargetStationId.equals(context.finalTargetStationId)) {
|
traceEvent(deviceNo, context, "TARGET_SWITCHED",
|
"任务目标站发生切换: " + context.finalTargetStationId + " -> " + commandTargetStationId,
|
buildDetails("fromTargetStationId", context.finalTargetStationId, "toTargetStationId",
|
commandTargetStationId),
|
false);
|
context.arrivalHandled = false;
|
}
|
context.finalTargetStationId = commandTargetStationId;
|
syncCurrentStationTarget(context.taskNo, context.currentStationId, context.finalTargetStationId);
|
}
|
|
if (!newPath.isEmpty() && startIndex < 0) {
|
traceEvent(deviceNo, context, "SEGMENT_IGNORED", "路径分段无法与当前运行上下文衔接,已忽略",
|
buildDetails("segmentPath", newPath, "currentStationId", context.currentStationId,
|
"queueTailStationId", lastInQueue, "ignoreReason", "PATH_NOT_CONNECTED"),
|
false);
|
context.latestAppendedPath.clear();
|
return;
|
}
|
|
List<Integer> appendedPath = new ArrayList<Integer>();
|
for (int i = startIndex; i < newPath.size(); i++) {
|
Integer stationId = newPath.get(i);
|
context.pendingPathQueue.offer(stationId);
|
appendedPath.add(stationId);
|
}
|
context.appendStitchedPath(appendedPath);
|
|
if (!appendedPath.isEmpty()) {
|
traceEvent(deviceNo, context, "SEGMENT_APPENDED",
|
"路径分段已追加到待执行队列,队列长度=" + context.pendingPathQueue.size(),
|
buildDetails("segmentPath", newPath, "appendedPath", appendedPath, "appendStartIndex",
|
startIndex, "queueSize", context.pendingPathQueue.size()),
|
false);
|
}
|
}
|
|
private void initializeTaskPosition(Integer deviceNo, TaskRuntimeContext context) {
|
Integer nextStationId = context.pendingPathQueue.peek();
|
if (nextStationId == null) {
|
return;
|
}
|
|
if (context.currentStationId == null) {
|
Integer actualCurrentStationId = findCurrentStationIdByTask(context.taskNo);
|
if (actualCurrentStationId != null) {
|
context.currentStationId = actualCurrentStationId;
|
context.initialized = true;
|
context.status = STATUS_RUNNING;
|
context.blockedStationId = null;
|
|
Integer actualDeviceNo = getDeviceNoByStationId(actualCurrentStationId);
|
if (actualDeviceNo != null) {
|
clearRunBlock(actualCurrentStationId, actualDeviceNo);
|
}
|
|
trimPendingPathToCurrent(context.pendingPathQueue, actualCurrentStationId);
|
if (actualCurrentStationId.equals(context.pendingPathQueue.peek())) {
|
context.pendingPathQueue.poll();
|
}
|
|
context.addPassedStation(actualCurrentStationId);
|
context.lastStepAt = System.currentTimeMillis();
|
traceEvent(deviceNo, context, "MOVE_INIT", "任务从当前实际站点恢复执行",
|
buildDetails("stationId", actualCurrentStationId, "recovered", true), false);
|
return;
|
}
|
}
|
|
context.currentStationId = nextStationId;
|
Integer currentDeviceNo = getDeviceNoByStationId(context.currentStationId);
|
if (currentDeviceNo == null) {
|
context.pendingPathQueue.poll();
|
return;
|
}
|
|
boolean result = initStationMove(context.taskNo, context.currentStationId, currentDeviceNo, context.taskNo,
|
context.finalTargetStationId, true, null);
|
if (!result) {
|
sleep(200);
|
return;
|
}
|
|
context.initialized = true;
|
context.status = STATUS_RUNNING;
|
context.pendingPathQueue.poll();
|
context.addPassedStation(context.currentStationId);
|
context.lastStepAt = System.currentTimeMillis();
|
traceEvent(deviceNo, context, "MOVE_INIT", "任务初始化起点站点",
|
buildDetails("stationId", context.currentStationId, "recovered", false), false);
|
sleep(500);
|
}
|
|
private boolean executeNextMove(Integer deviceNo, TaskRuntimeContext context) {
|
Integer nextStationId = context.pendingPathQueue.peek();
|
if (nextStationId == null || context.currentStationId == null) {
|
return true;
|
}
|
|
Integer currentDeviceNo = getDeviceNoByStationId(context.currentStationId);
|
Integer nextDeviceNo = getDeviceNoByStationId(nextStationId);
|
if (currentDeviceNo == null || nextDeviceNo == null) {
|
context.pendingPathQueue.poll();
|
return true;
|
}
|
|
boolean moveSuccess = stationMoveToNext(context.taskNo, context.currentStationId, currentDeviceNo,
|
nextStationId, nextDeviceNo, context.taskNo, context.finalTargetStationId);
|
if (moveSuccess) {
|
Integer previousStationId = context.currentStationId;
|
context.currentStationId = nextStationId;
|
context.pendingPathQueue.poll();
|
context.addPassedStation(nextStationId);
|
context.arrivalHandled = false;
|
context.blockedStationId = null;
|
context.status = STATUS_RUNNING;
|
context.lastStepAt = System.currentTimeMillis();
|
traceEvent(deviceNo, context, "MOVE_STEP_OK", "任务完成一步站点移动",
|
buildDetails("fromStationId", previousStationId, "toStationId", nextStationId,
|
"remainingPendingPath", context.getPendingStationIds()),
|
false);
|
sleep(1000);
|
return true;
|
}
|
|
if (!checkTaskNoInArea(context.taskNo) && getFakeAllowCheckBlock()
|
&& System.currentTimeMillis() - context.lastStepAt > getFakeRunBlockTimeoutMs()) {
|
boolean blocked = runBlockStation(context.taskNo, context.currentStationId, currentDeviceNo, context.taskNo,
|
context.currentStationId);
|
if (blocked) {
|
context.blockedStationId = context.currentStationId;
|
context.status = STATUS_BLOCKED;
|
context.pendingPathQueue.clear();
|
traceEvent(deviceNo, context, "RUN_BLOCKED", "任务在当前站点被标记为堵塞",
|
buildDetails("blockedStationId", context.currentStationId), false);
|
return false;
|
}
|
}
|
|
sleep(500);
|
return true;
|
}
|
|
private boolean handleIdleState(Integer deviceNo, TaskRuntimeContext context) {
|
if (context.currentStationId != null && context.finalTargetStationId != null
|
&& context.currentStationId.equals(context.finalTargetStationId)) {
|
if (!context.arrivalHandled) {
|
boolean barcodeGenerated = false;
|
if (context.generateBarcode) {
|
Integer targetDeviceNo = getDeviceNoByStationId(context.finalTargetStationId);
|
if (targetDeviceNo != null) {
|
barcodeGenerated = generateStationBarcode(context.taskNo, context.finalTargetStationId,
|
targetDeviceNo);
|
}
|
}
|
context.arrivalHandled = true;
|
traceEvent(deviceNo, context, "ARRIVED", "任务到达最终目标站点",
|
buildDetails("stationId", context.currentStationId, "barcodeGenerated", barcodeGenerated),
|
false);
|
}
|
context.status = STATUS_FINISHED;
|
return true;
|
}
|
|
Long lastTime = taskLastUpdateTime.get(context.taskNo);
|
if (lastTime != null && System.currentTimeMillis() - lastTime > WAIT_SEGMENT_TIMEOUT_MS) {
|
context.status = STATUS_TIMEOUT;
|
traceEvent(deviceNo, context, "WAIT_TIMEOUT", "等待新的路径分段超时",
|
buildDetails("timeoutMs", WAIT_SEGMENT_TIMEOUT_MS, "currentStationId", context.currentStationId,
|
"targetStationId", context.finalTargetStationId),
|
false);
|
return true;
|
}
|
return false;
|
}
|
|
private List<Integer> normalizePath(List<Integer> path) {
|
List<Integer> result = new ArrayList<Integer>();
|
if (path == null) {
|
return result;
|
}
|
for (Integer stationId : path) {
|
if (stationId != null) {
|
result.add(stationId);
|
}
|
}
|
return result;
|
}
|
|
private void trimPendingPathToCurrent(LinkedBlockingQueue<Integer> queue, Integer currentStationId) {
|
if (queue == null || currentStationId == null || queue.isEmpty()) {
|
return;
|
}
|
List<Integer> snapshot = new ArrayList<Integer>(queue);
|
int index = snapshot.indexOf(currentStationId);
|
if (index <= 0) {
|
return;
|
}
|
for (int i = 0; i < index; i++) {
|
queue.poll();
|
}
|
}
|
|
private boolean hasTaskReset(Integer taskNo) {
|
if (redisUtil == null || taskNo == null) {
|
return false;
|
}
|
Object cancel = redisUtil.get(RedisKeyType.DEVICE_STATION_MOVE_RESET.key + taskNo);
|
return cancel != null;
|
}
|
|
private Integer getLastInQueue(LinkedBlockingQueue<Integer> queue) {
|
Integer last = null;
|
for (Integer item : queue) {
|
last = item;
|
}
|
return last;
|
}
|
|
private int getPathAppendStartIndex(List<Integer> newPath, Integer currentStationId, Integer lastInQueue) {
|
if (newPath == null || newPath.isEmpty()) {
|
return 0;
|
}
|
|
if (lastInQueue != null) {
|
int idx = newPath.lastIndexOf(lastInQueue);
|
if (idx >= 0) {
|
return idx + 1;
|
}
|
}
|
|
if (currentStationId != null) {
|
int idx = newPath.lastIndexOf(currentStationId);
|
if (idx >= 0) {
|
return idx + 1;
|
}
|
return -1;
|
}
|
|
if (lastInQueue != null) {
|
return -1;
|
}
|
|
return 0;
|
}
|
|
private void syncCurrentStationTarget(Integer taskNo, Integer currentStationId, Integer targetStationId) {
|
if (currentStationId == null || targetStationId == null) {
|
return;
|
}
|
Integer currentDeviceNo = getDeviceNoByStationId(currentStationId);
|
if (currentDeviceNo == null) {
|
return;
|
}
|
|
lockStations(currentStationId);
|
try {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentDeviceNo);
|
if (statusList == null) {
|
return;
|
}
|
|
ZyStationStatusEntity currentStatus = statusList.stream()
|
.filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
|
if (currentStatus == null) {
|
return;
|
}
|
|
if (currentStatus.getTaskNo() != null && currentStatus.getTaskNo() > 0
|
&& !currentStatus.getTaskNo().equals(taskNo) && currentStatus.isLoading()) {
|
return;
|
}
|
|
updateStationDataInternal(currentStationId, currentDeviceNo, taskNo, targetStationId, null, null, null);
|
} finally {
|
unlockStations(currentStationId);
|
}
|
}
|
|
@SuppressWarnings("unchecked")
|
private boolean getFakeAllowCheckBlock() {
|
boolean fakeAllowCheckBlock = true;
|
Object systemConfigMapObj = redisUtil == null ? null : redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
|
if (systemConfigMapObj instanceof Map) {
|
Map<String, String> systemConfigMap = (Map<String, String>) systemConfigMapObj;
|
String value = systemConfigMap.get("fakeAllowCheckBlock");
|
if (value != null && !"Y".equals(value)) {
|
fakeAllowCheckBlock = false;
|
}
|
}
|
return fakeAllowCheckBlock;
|
}
|
|
private long getFakeRunBlockTimeoutMs() {
|
long timeoutMs = DEFAULT_FAKE_RUN_BLOCK_TIMEOUT_MS;
|
Object systemConfigMapObj = redisUtil == null ? null : redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
|
if (systemConfigMapObj instanceof Map) {
|
Map<?, ?> systemConfigMap = (Map<?, ?>) systemConfigMapObj;
|
Object value = systemConfigMap.get("fakeRunBlockTimeoutMs");
|
if (value != null) {
|
try {
|
long parsed = Long.parseLong(String.valueOf(value).trim());
|
if (parsed > 0) {
|
timeoutMs = parsed;
|
}
|
} catch (Exception ignore) {
|
}
|
}
|
}
|
return timeoutMs;
|
}
|
|
private void handleCommand(Integer deviceNo, StationCommand command) {
|
News.info("[WCS Debug] 站点仿真模拟(V3)已启动,命令数据={}", JSON.toJSONString(command));
|
Integer taskNo = command.getTaskNo();
|
Integer stationId = command.getStationId();
|
Integer targetStationId = command.getTargetStaNo();
|
|
if (command.getCommandType() == StationCommandType.RESET) {
|
resetStation(deviceNo, stationId);
|
return;
|
}
|
|
if (command.getCommandType() == StationCommandType.WRITE_INFO) {
|
if (command.getBarcode() != null) {
|
updateStationBarcode(deviceNo, stationId, command.getBarcode());
|
return;
|
}
|
if (taskNo == 9998 && targetStationId == 0) {
|
generateFakeOutStationData(deviceNo, stationId);
|
return;
|
}
|
}
|
|
if (taskNo != null && taskNo > 0 && taskNo != 9999 && taskNo != 9998 && stationId != null
|
&& stationId.equals(targetStationId)) {
|
generateStationData(deviceNo, taskNo, stationId, targetStationId);
|
}
|
}
|
|
private void generateFakeOutStationData(Integer deviceNo, Integer stationId) {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
|
if (statusList == null) {
|
return;
|
}
|
ZyStationStatusEntity status = statusList.stream()
|
.filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
|
if (status == null) {
|
return;
|
}
|
synchronized (status) {
|
status.setLoading(true);
|
}
|
}
|
|
private void generateStationData(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
|
if (statusList == null) {
|
return;
|
}
|
ZyStationStatusEntity status = statusList.stream()
|
.filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
|
if (status == null) {
|
return;
|
}
|
synchronized (status) {
|
status.setTaskNo(taskNo);
|
status.setTargetStaNo(targetStationId);
|
}
|
}
|
|
private void resetStation(Integer deviceNo, Integer stationId) {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
|
if (statusList == null) {
|
return;
|
}
|
ZyStationStatusEntity status = statusList.stream()
|
.filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
|
if (status == null) {
|
return;
|
}
|
synchronized (status) {
|
status.setTaskNo(0);
|
status.setLoading(false);
|
status.setBarcode("");
|
}
|
}
|
|
private void updateStationBarcode(Integer deviceNo, Integer stationId, String barcode) {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
|
if (statusList == null) {
|
return;
|
}
|
ZyStationStatusEntity status = statusList.stream()
|
.filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
|
if (status == null) {
|
return;
|
}
|
synchronized (status) {
|
status.setBarcode(barcode);
|
}
|
}
|
|
private Integer getDeviceNoByStationId(Integer stationId) {
|
for (Map.Entry<Integer, List<ZyStationStatusEntity>> entry : deviceStatusMap.entrySet()) {
|
List<ZyStationStatusEntity> list = entry.getValue();
|
if (list == null) {
|
continue;
|
}
|
for (ZyStationStatusEntity entity : list) {
|
if (entity.getStationId() != null && entity.getStationId().equals(stationId)) {
|
return entry.getKey();
|
}
|
}
|
}
|
return null;
|
}
|
|
private Integer findCurrentStationIdByTask(Integer taskNo) {
|
for (List<ZyStationStatusEntity> list : deviceStatusMap.values()) {
|
if (list == null) {
|
continue;
|
}
|
for (ZyStationStatusEntity entity : list) {
|
if (entity.getTaskNo() != null && entity.getTaskNo().equals(taskNo) && entity.isLoading()) {
|
return entity.getStationId();
|
}
|
}
|
}
|
return null;
|
}
|
|
private void sleep(long ms) {
|
try {
|
Thread.sleep(ms);
|
} catch (InterruptedException e) {
|
Thread.currentThread().interrupt();
|
}
|
}
|
|
private ReentrantLock getStationLock(Integer stationId) {
|
return stationLocks.computeIfAbsent(stationId, key -> new ReentrantLock());
|
}
|
|
private void lockStations(Integer... stationIds) {
|
Integer[] sorted = Arrays.copyOf(stationIds, stationIds.length);
|
Arrays.sort(sorted);
|
for (Integer stationId : sorted) {
|
getStationLock(stationId).lock();
|
}
|
}
|
|
private void unlockStations(Integer... stationIds) {
|
Integer[] sorted = Arrays.copyOf(stationIds, stationIds.length);
|
Arrays.sort(sorted);
|
for (int i = sorted.length - 1; i >= 0; i--) {
|
getStationLock(sorted[i]).unlock();
|
}
|
}
|
|
private boolean updateStationDataInternal(Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStaNo,
|
Boolean isLoading, String barcode, Boolean runBlock) {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
|
if (statusList == null) {
|
return false;
|
}
|
ZyStationStatusEntity currentStatus = statusList.stream()
|
.filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
|
if (currentStatus == null) {
|
return false;
|
}
|
|
if (taskNo != null) {
|
currentStatus.setTaskNo(taskNo);
|
}
|
if (targetStaNo != null) {
|
currentStatus.setTargetStaNo(targetStaNo);
|
}
|
if (isLoading != null) {
|
currentStatus.setLoading(isLoading);
|
}
|
if (barcode != null) {
|
currentStatus.setBarcode(barcode);
|
}
|
if (runBlock != null) {
|
currentStatus.setRunBlock(runBlock);
|
}
|
return true;
|
}
|
|
public boolean initStationMove(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo,
|
Integer taskNo, Integer targetStationId, Boolean isLoading, String barcode) {
|
lockStations(currentStationId);
|
try {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo);
|
if (statusList == null) {
|
return false;
|
}
|
ZyStationStatusEntity currentStatus = statusList.stream()
|
.filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
|
if (currentStatus == null) {
|
return false;
|
}
|
if (currentStatus.getTaskNo() != null && currentStatus.getTaskNo() > 0
|
&& !currentStatus.getTaskNo().equals(taskNo) && currentStatus.isLoading()) {
|
return false;
|
}
|
|
return updateStationDataInternal(currentStationId, currentStationDeviceNo, taskNo, targetStationId,
|
isLoading, barcode, false);
|
} finally {
|
unlockStations(currentStationId);
|
}
|
}
|
|
public boolean stationMoveToNext(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo,
|
Integer nextStationId, Integer nextStationDeviceNo, Integer taskNo, Integer targetStaNo) {
|
lockStations(currentStationId, nextStationId);
|
try {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo);
|
List<ZyStationStatusEntity> nextStatusList = deviceStatusMap.get(nextStationDeviceNo);
|
if (statusList == null || nextStatusList == null) {
|
return false;
|
}
|
|
ZyStationStatusEntity currentStatus = statusList.stream()
|
.filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
|
ZyStationStatusEntity nextStatus = nextStatusList.stream()
|
.filter(item -> item.getStationId().equals(nextStationId)).findFirst().orElse(null);
|
if (currentStatus == null || nextStatus == null) {
|
return false;
|
}
|
if (nextStatus.getTaskNo() != null && nextStatus.getTaskNo() > 0 || nextStatus.isLoading()) {
|
return false;
|
}
|
|
boolean result = updateStationDataInternal(nextStationId, nextStationDeviceNo, taskNo, targetStaNo, true,
|
null, false);
|
if (!result) {
|
return false;
|
}
|
return updateStationDataInternal(currentStationId, currentStationDeviceNo, 0, 0, false, "", false);
|
} finally {
|
unlockStations(currentStationId, nextStationId);
|
}
|
}
|
|
public boolean generateStationBarcode(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo) {
|
lockStations(currentStationId);
|
try {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo);
|
if (statusList == null) {
|
return false;
|
}
|
ZyStationStatusEntity currentStatus = statusList.stream()
|
.filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
|
if (currentStatus == null) {
|
return false;
|
}
|
|
Random random = new Random();
|
String barcodeTime = String.valueOf(System.currentTimeMillis());
|
String barcode = String.valueOf(random.nextInt(10)) + String.valueOf(random.nextInt(10))
|
+ barcodeTime.substring(7);
|
return updateStationDataInternal(currentStationId, currentStationDeviceNo, null, null, null, barcode, null);
|
} finally {
|
unlockStations(currentStationId);
|
}
|
}
|
|
public boolean clearStation(Integer deviceNo, Integer lockTaskNo, Integer currentStationId) {
|
lockStations(currentStationId);
|
try {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
|
if (statusList == null) {
|
return false;
|
}
|
ZyStationStatusEntity currentStatus = statusList.stream()
|
.filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
|
if (currentStatus == null) {
|
return false;
|
}
|
return updateStationDataInternal(currentStationId, deviceNo, 0, 0, false, "", false);
|
} finally {
|
unlockStations(currentStationId);
|
}
|
}
|
|
public boolean runBlockStation(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo,
|
Integer taskNo, Integer blockStationId) {
|
lockStations(currentStationId);
|
try {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo);
|
if (statusList == null) {
|
return false;
|
}
|
ZyStationStatusEntity currentStatus = statusList.stream()
|
.filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null);
|
if (currentStatus == null) {
|
return false;
|
}
|
return updateStationDataInternal(currentStationId, currentStationDeviceNo, taskNo, blockStationId, true, "",
|
true);
|
} finally {
|
unlockStations(currentStationId);
|
}
|
}
|
|
public void clearRunBlock(Integer stationId, Integer deviceNo) {
|
lockStations(stationId);
|
try {
|
List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo);
|
if (statusList == null) {
|
return;
|
}
|
ZyStationStatusEntity currentStatus = statusList.stream()
|
.filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null);
|
if (currentStatus == null) {
|
return;
|
}
|
if (currentStatus.isRunBlock()) {
|
currentStatus.setRunBlock(false);
|
News.info("[WCS Debug] 站点{}堵塞标记已清除", stationId);
|
}
|
} finally {
|
unlockStations(stationId);
|
}
|
}
|
|
private boolean checkTaskNoInArea(Integer taskNo) {
|
if (taskNo == null || redisUtil == null) {
|
return false;
|
}
|
|
Object fakeTaskNoAreaObj = redisUtil.get(RedisKeyType.FAKE_TASK_NO_AREA.key);
|
if (fakeTaskNoAreaObj == null) {
|
return false;
|
}
|
|
JSONObject data = JSON.parseObject(String.valueOf(fakeTaskNoAreaObj));
|
Integer start = data.getInteger("start");
|
Integer end = data.getInteger("end");
|
if (start == null || end == null) {
|
return false;
|
}
|
return taskNo >= start && taskNo <= end;
|
}
|
|
private Map<String, Object> buildDetails(Object... keyValues) {
|
Map<String, Object> details = new LinkedHashMap<String, Object>();
|
if (keyValues == null) {
|
return details;
|
}
|
for (int i = 0; i + 1 < keyValues.length; i += 2) {
|
Object key = keyValues[i];
|
if (key != null) {
|
details.put(String.valueOf(key), keyValues[i + 1]);
|
}
|
}
|
return details;
|
}
|
|
private void traceEvent(Integer deviceNo, TaskRuntimeContext context, String eventType, String message,
|
Map<String, Object> details, boolean terminal) {
|
if (context == null || context.taskNo == null) {
|
return;
|
}
|
try {
|
FakeTaskTraceRegistry registry = SpringUtils.getBean(FakeTaskTraceRegistry.class);
|
if (registry == null) {
|
return;
|
}
|
registry.record(context.taskNo, context.threadImpl != null ? context.threadImpl : getThreadImpl(deviceNo),
|
context.status, context.startStationId, context.currentStationId, context.finalTargetStationId,
|
context.blockedStationId, context.getStitchedPathStationIds(), context.getPassedStationIds(),
|
context.getPendingStationIds(), context.getLatestAppendedPath(), eventType, message, details,
|
terminal);
|
} catch (Exception ignore) {
|
}
|
}
|
|
private String getThreadImpl(Integer deviceNo) {
|
DeviceConfig deviceConfig = deviceConfigMap.get(deviceNo);
|
return deviceConfig == null ? "" : deviceConfig.getThreadImpl();
|
}
|
|
private boolean isTerminalStatus(String status) {
|
return STATUS_BLOCKED.equals(status) || STATUS_CANCELLED.equals(status) || STATUS_TIMEOUT.equals(status)
|
|| STATUS_FINISHED.equals(status);
|
}
|
|
private static class TaskRuntimeContext {
|
|
private final Integer taskNo;
|
private final String threadImpl;
|
private final LinkedBlockingQueue<Integer> pendingPathQueue = new LinkedBlockingQueue<Integer>();
|
private final List<Integer> stitchedPathStationIds = new ArrayList<Integer>();
|
private final List<Integer> passedStationIds = new ArrayList<Integer>();
|
private final List<Integer> latestAppendedPath = new ArrayList<Integer>();
|
|
private Integer startStationId;
|
private Integer currentStationId;
|
private Integer finalTargetStationId;
|
private Integer blockedStationId;
|
private boolean generateBarcode;
|
private boolean initialized;
|
private boolean arrivalHandled;
|
private long lastStepAt = System.currentTimeMillis();
|
private long lastCommandAt = System.currentTimeMillis();
|
private String status = STATUS_WAITING;
|
|
private TaskRuntimeContext(Integer taskNo, String threadImpl) {
|
this.taskNo = taskNo;
|
this.threadImpl = threadImpl;
|
}
|
|
private void setStartStationIdIfAbsent(Integer stationId) {
|
if (startStationId == null && stationId != null) {
|
startStationId = stationId;
|
}
|
}
|
|
private void appendStitchedPath(List<Integer> path) {
|
latestAppendedPath.clear();
|
if (path == null) {
|
return;
|
}
|
for (Integer stationId : path) {
|
if (stationId == null) {
|
continue;
|
}
|
latestAppendedPath.add(stationId);
|
if (stitchedPathStationIds.isEmpty()
|
|| !stationId.equals(stitchedPathStationIds.get(stitchedPathStationIds.size() - 1))) {
|
stitchedPathStationIds.add(stationId);
|
}
|
}
|
}
|
|
private void addPassedStation(Integer stationId) {
|
if (stationId == null) {
|
return;
|
}
|
if (passedStationIds.isEmpty()
|
|| !stationId.equals(passedStationIds.get(passedStationIds.size() - 1))) {
|
passedStationIds.add(stationId);
|
}
|
}
|
|
private List<Integer> getPendingStationIds() {
|
return new ArrayList<Integer>(pendingPathQueue);
|
}
|
|
private List<Integer> getPassedStationIds() {
|
return new ArrayList<Integer>(passedStationIds);
|
}
|
|
private List<Integer> getStitchedPathStationIds() {
|
return new ArrayList<Integer>(stitchedPathStationIds);
|
}
|
|
private List<Integer> getLatestAppendedPath() {
|
return new ArrayList<Integer>(latestAppendedPath);
|
}
|
}
|
}
|