| | |
| | | WcsDiagnosisRequest request = aiUtils.makeAiRequest(1000, "对当前系统进行巡检,如果有异常情况就进行详细的分析,如果没有异常情况则当成一次检查\n\n"); |
| | | wcsDiagnosisService.diagnoseStream(request, emitter); |
| | | } catch (Exception e) { |
| | | emitter.completeWithError(e); |
| | | try { emitter.complete(); } catch (Exception ignore) {} |
| | | } |
| | | }).start(); |
| | | |
| | |
| | | WcsDiagnosisRequest request = aiUtils.makeAiRequest(100, null); |
| | | wcsDiagnosisService.askStream(request, prompt, chatId, reset, emitter); |
| | | } catch (Exception e) { |
| | | emitter.completeWithError(e); |
| | | try { emitter.complete(); } catch (Exception ignore) {} |
| | | } |
| | | }).start(); |
| | | return emitter; |
| | |
| | | package com.zy.ai.service; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.zy.ai.entity.ChatCompletionRequest; |
| | | import com.zy.ai.entity.ChatCompletionResponse; |
| | | import com.zy.ai.entity.LlmCallLog; |
| | | import com.zy.ai.entity.LlmRouteConfig; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.web.reactive.function.client.WebClient; |
| | | import reactor.core.publisher.Mono; |
| | | import reactor.core.publisher.Flux; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.function.Consumer; |
| | | import java.util.UUID; |
| | | import java.util.concurrent.LinkedBlockingQueue; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import java.util.function.Consumer; |
| | | |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class LlmChatService { |
| | | |
| | | private final WebClient llmWebClient; |
| | | private static final int LOG_TEXT_LIMIT = 16000; |
| | | |
| | | @Value("${llm.api-key}") |
| | | private String apiKey; |
| | | private final LlmRoutingService llmRoutingService; |
| | | private final LlmCallLogService llmCallLogService; |
| | | |
| | | @Value("${llm.model}") |
| | | private String model; |
| | | @Value("${llm.base-url:}") |
| | | private String fallbackBaseUrl; |
| | | |
| | | @Value("${llm.pythonPlatformUrl}") |
| | | private String pythonPlatformUrl; |
| | | @Value("${llm.api-key:}") |
| | | private String fallbackApiKey; |
| | | |
| | | @Value("${llm.thinking}") |
| | | private String thinking; |
| | | @Value("${llm.model:}") |
| | | private String fallbackModel; |
| | | |
| | | @Value("${llm.thinking:false}") |
| | | private String fallbackThinking; |
| | | |
| | | /** |
| | | * 通用对话方法:传入 messages,返回大模型文本回复 |
| | |
| | | Integer maxTokens) { |
| | | |
| | | ChatCompletionRequest req = new ChatCompletionRequest(); |
| | | req.setModel(model); |
| | | req.setMessages(messages); |
| | | req.setTemperature(temperature != null ? temperature : 0.3); |
| | | req.setMax_tokens(maxTokens != null ? maxTokens : 1024); |
| | | req.setStream(false); |
| | | |
| | | ChatCompletionResponse response = llmWebClient.post() |
| | | .uri("/chat/completions") |
| | | .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey) |
| | | .contentType(MediaType.APPLICATION_JSON) |
| | | .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_EVENT_STREAM) |
| | | .bodyValue(req) |
| | | .exchangeToMono(resp -> resp.bodyToFlux(String.class) |
| | | .collectList() |
| | | .map(list -> { |
| | | String payload = String.join("\n\n", list); |
| | | return parseCompletion(payload); |
| | | })) |
| | | .doOnError(ex -> log.error("调用 LLM 失败", ex)) |
| | | .onErrorResume(ex -> Mono.empty()) |
| | | .block(); |
| | | ChatCompletionResponse response = complete(req, "chat"); |
| | | |
| | | if (response == null || |
| | | response.getChoices() == null || |
| | |
| | | Integer maxTokens, |
| | | List<Object> tools) { |
| | | ChatCompletionRequest req = new ChatCompletionRequest(); |
| | | req.setModel(model); |
| | | req.setMessages(messages); |
| | | req.setTemperature(temperature != null ? temperature : 0.3); |
| | | req.setMax_tokens(maxTokens != null ? maxTokens : 1024); |
| | | req.setStream(false); |
| | | |
| | | if(thinking.equals("enable")) { |
| | | ChatCompletionRequest.Thinking thinking = new ChatCompletionRequest.Thinking(); |
| | | thinking.setType("enable"); |
| | | req.setThinking(thinking); |
| | | } |
| | | if (tools != null && !tools.isEmpty()) { |
| | | req.setTools(tools); |
| | | req.setTool_choice("auto"); |
| | | } |
| | | return complete(req); |
| | | return complete(req, tools != null && !tools.isEmpty() ? "chat_completion_tools" : "chat_completion"); |
| | | } |
| | | |
| | | public ChatCompletionResponse complete(ChatCompletionRequest req) { |
| | | try { |
| | | return llmWebClient.post() |
| | | .uri("/chat/completions") |
| | | .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey) |
| | | .contentType(MediaType.APPLICATION_JSON) |
| | | .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_EVENT_STREAM) |
| | | .bodyValue(req) |
| | | .exchangeToMono(resp -> resp.bodyToFlux(String.class) |
| | | .collectList() |
| | | .map(list -> { |
| | | String payload = String.join("\n\n", list); |
| | | return parseCompletion(payload); |
| | | })) |
| | | .doOnError(ex -> log.error("调用 LLM 失败", ex)) |
| | | .onErrorResume(ex -> Mono.empty()) |
| | | .block(); |
| | | } catch (Exception e) { |
| | | log.error("调用 LLM 失败", e); |
| | | return complete(req, "completion"); |
| | | } |
| | | |
| | | private ChatCompletionResponse complete(ChatCompletionRequest req, String scene) { |
| | | String traceId = nextTraceId(); |
| | | List<ResolvedRoute> routes = resolveRoutes(); |
| | | if (routes.isEmpty()) { |
| | | log.error("调用 LLM 失败: 未配置可用 LLM 路由"); |
| | | recordCall(traceId, scene, false, 1, null, false, null, 0L, req, null, "none", |
| | | new RuntimeException("未配置可用 LLM 路由"), "no_route"); |
| | | return null; |
| | | } |
| | | |
| | | Throwable last = null; |
| | | for (int i = 0; i < routes.size(); i++) { |
| | | ResolvedRoute route = routes.get(i); |
| | | boolean hasNext = i < routes.size() - 1; |
| | | ChatCompletionRequest routeReq = applyRoute(cloneRequest(req), route, false); |
| | | long start = System.currentTimeMillis(); |
| | | try { |
| | | CompletionCallResult callResult = callCompletion(route, routeReq); |
| | | ChatCompletionResponse resp = callResult.response; |
| | | if (!isValidCompletion(resp)) { |
| | | RuntimeException ex = new RuntimeException("LLM 响应为空"); |
| | | boolean canSwitch = shouldSwitch(route, false); |
| | | markFailure(route, ex, canSwitch); |
| | | recordCall(traceId, scene, false, i + 1, route, false, callResult.statusCode, |
| | | System.currentTimeMillis() - start, routeReq, callResult.payload, "error", ex, |
| | | "invalid_completion"); |
| | | if (hasNext && canSwitch) { |
| | | log.warn("LLM 切换到下一路由, current={}, reason={}", route.tag(), ex.getMessage()); |
| | | continue; |
| | | } |
| | | log.error("调用 LLM 失败, route={}", route.tag(), ex); |
| | | last = ex; |
| | | break; |
| | | } |
| | | markSuccess(route); |
| | | recordCall(traceId, scene, false, i + 1, route, true, callResult.statusCode, |
| | | System.currentTimeMillis() - start, routeReq, buildResponseText(resp, callResult.payload), |
| | | "none", null, null); |
| | | return resp; |
| | | } catch (Throwable ex) { |
| | | last = ex; |
| | | boolean quota = isQuotaExhausted(ex); |
| | | boolean canSwitch = shouldSwitch(route, quota); |
| | | markFailure(route, ex, canSwitch); |
| | | recordCall(traceId, scene, false, i + 1, route, false, statusCodeOf(ex), |
| | | System.currentTimeMillis() - start, routeReq, responseBodyOf(ex), |
| | | quota ? "quota" : "error", ex, null); |
| | | if (hasNext && canSwitch) { |
| | | log.warn("LLM 切换到下一路由, current={}, reason={}", route.tag(), errorText(ex)); |
| | | continue; |
| | | } |
| | | log.error("调用 LLM 失败, route={}", route.tag(), ex); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (last != null) { |
| | | log.error("调用 LLM 全部路由失败: {}", errorText(last)); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | public void chatStream(List<ChatCompletionRequest.Message> messages, |
| | |
| | | Consumer<Throwable> onError) { |
| | | |
| | | ChatCompletionRequest req = new ChatCompletionRequest(); |
| | | req.setModel(model); |
| | | req.setMessages(messages); |
| | | req.setTemperature(temperature != null ? temperature : 0.3); |
| | | req.setMax_tokens(maxTokens != null ? maxTokens : 1024); |
| | | req.setStream(true); |
| | | |
| | | |
| | | Flux<String> flux = llmWebClient.post() |
| | | .uri("/chat/completions") |
| | | .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey) |
| | | .contentType(MediaType.APPLICATION_JSON) |
| | | .accept(MediaType.TEXT_EVENT_STREAM) |
| | | .bodyValue(req) |
| | | .retrieve() |
| | | .bodyToFlux(String.class) |
| | | .doOnError(ex -> log.error("调用 LLM 流式失败", ex)); |
| | | |
| | | AtomicBoolean doneSeen = new AtomicBoolean(false); |
| | | AtomicBoolean errorSeen = new AtomicBoolean(false); |
| | | LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(); |
| | | |
| | | Thread drain = new Thread(() -> { |
| | | try { |
| | | while (true) { |
| | | String s = queue.poll(2, TimeUnit.SECONDS); |
| | | if (s != null) { |
| | | try { onChunk.accept(s); } catch (Exception ignore) {} |
| | | } |
| | | if (doneSeen.get() && queue.isEmpty()) { |
| | | if (!errorSeen.get()) { |
| | | try { if (onComplete != null) onComplete.run(); } catch (Exception ignore) {} |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | } catch (InterruptedException ignore) { |
| | | ignore.printStackTrace(); |
| | | } |
| | | }); |
| | | drain.setDaemon(true); |
| | | drain.start(); |
| | | |
| | | flux.subscribe(payload -> { |
| | | if (payload == null || payload.isEmpty()) return; |
| | | String[] events = payload.split("\\r?\\n\\r?\\n"); |
| | | for (String part : events) { |
| | | String s = part; |
| | | if (s == null || s.isEmpty()) continue; |
| | | if (s.startsWith("data:")) { |
| | | s = s.substring(5); |
| | | if (s.startsWith(" ")) s = s.substring(1); |
| | | } |
| | | if ("[DONE]".equals(s.trim())) { |
| | | doneSeen.set(true); |
| | | continue; |
| | | } |
| | | try { |
| | | JSONObject obj = JSON.parseObject(s); |
| | | JSONArray choices = obj.getJSONArray("choices"); |
| | | if (choices != null && !choices.isEmpty()) { |
| | | JSONObject c0 = choices.getJSONObject(0); |
| | | JSONObject delta = c0.getJSONObject("delta"); |
| | | if (delta != null) { |
| | | String content = delta.getString("content"); |
| | | if (content != null) { |
| | | try { queue.offer(content); } catch (Exception ignore) {} |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | }, err -> { |
| | | errorSeen.set(true); |
| | | doneSeen.set(true); |
| | | if (onError != null) onError.accept(err); |
| | | }, () -> { |
| | | if (!doneSeen.get()) { |
| | | errorSeen.set(true); |
| | | doneSeen.set(true); |
| | | if (onError != null) onError.accept(new RuntimeException("LLM 流意外完成")); |
| | | } else { |
| | | doneSeen.set(true); |
| | | } |
| | | }); |
| | | streamWithFailover(req, onChunk, onComplete, onError, "chat_stream"); |
| | | } |
| | | |
| | | public void chatStreamWithTools(List<ChatCompletionRequest.Message> messages, |
| | |
| | | Runnable onComplete, |
| | | Consumer<Throwable> onError) { |
| | | ChatCompletionRequest req = new ChatCompletionRequest(); |
| | | req.setModel(model); |
| | | req.setMessages(messages); |
| | | req.setTemperature(temperature != null ? temperature : 0.3); |
| | | req.setMax_tokens(maxTokens != null ? maxTokens : 1024); |
| | | req.setStream(true); |
| | | if(thinking.equals("enable")) { |
| | | ChatCompletionRequest.Thinking thinking = new ChatCompletionRequest.Thinking(); |
| | | thinking.setType("enable"); |
| | | req.setThinking(thinking); |
| | | } |
| | | if (tools != null && !tools.isEmpty()) { |
| | | req.setTools(tools); |
| | | req.setTool_choice("auto"); |
| | | } |
| | | Flux<String> flux = llmWebClient.post() |
| | | .uri("/chat/completions") |
| | | .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey) |
| | | .contentType(MediaType.APPLICATION_JSON) |
| | | .accept(MediaType.TEXT_EVENT_STREAM) |
| | | .bodyValue(req) |
| | | .retrieve() |
| | | .bodyToFlux(String.class) |
| | | .doOnError(ex -> log.error("调用 LLM 流式失败", ex)); |
| | | |
| | | AtomicBoolean doneSeen = new AtomicBoolean(false); |
| | | AtomicBoolean errorSeen = new AtomicBoolean(false); |
| | | LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(); |
| | | |
| | | Thread drain = new Thread(() -> { |
| | | try { |
| | | while (true) { |
| | | String s = queue.poll(5, TimeUnit.SECONDS); |
| | | if (s != null) { |
| | | try { onChunk.accept(s); } catch (Exception ignore) {} |
| | | } |
| | | if (doneSeen.get() && queue.isEmpty()) { |
| | | if (!errorSeen.get()) { |
| | | try { if (onComplete != null) onComplete.run(); } catch (Exception ignore) {} |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | } catch (InterruptedException ignore) { |
| | | ignore.printStackTrace(); |
| | | } |
| | | }); |
| | | drain.setDaemon(true); |
| | | drain.start(); |
| | | |
| | | flux.subscribe(payload -> { |
| | | if (payload == null || payload.isEmpty()) return; |
| | | String[] events = payload.split("\\r?\\n\\r?\\n"); |
| | | for (String part : events) { |
| | | String s = part; |
| | | if (s == null || s.isEmpty()) continue; |
| | | if (s.startsWith("data:")) { |
| | | s = s.substring(5); |
| | | if (s.startsWith(" ")) s = s.substring(1); |
| | | } |
| | | if ("[DONE]".equals(s.trim())) { |
| | | doneSeen.set(true); |
| | | continue; |
| | | } |
| | | try { |
| | | JSONObject obj = JSON.parseObject(s); |
| | | JSONArray choices = obj.getJSONArray("choices"); |
| | | if (choices != null && !choices.isEmpty()) { |
| | | JSONObject c0 = choices.getJSONObject(0); |
| | | JSONObject delta = c0.getJSONObject("delta"); |
| | | if (delta != null) { |
| | | String content = delta.getString("content"); |
| | | if (content != null) { |
| | | try { queue.offer(content); } catch (Exception ignore) {} |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | }, err -> { |
| | | errorSeen.set(true); |
| | | doneSeen.set(true); |
| | | if (onError != null) onError.accept(err); |
| | | }, () -> { |
| | | if (!doneSeen.get()) { |
| | | errorSeen.set(true); |
| | | doneSeen.set(true); |
| | | if (onError != null) onError.accept(new RuntimeException("LLM 流意外完成")); |
| | | } else { |
| | | doneSeen.set(true); |
| | | } |
| | | }); |
| | | streamWithFailover(req, onChunk, onComplete, onError, tools != null && !tools.isEmpty() ? "chat_stream_tools" : "chat_stream"); |
| | | } |
| | | |
| | | public void chatStreamRunPython(String prompt, String chatId, Consumer<String> onChunk, |
| | | private void streamWithFailover(ChatCompletionRequest req, |
| | | Consumer<String> onChunk, |
| | | Runnable onComplete, |
| | | Consumer<Throwable> onError) { |
| | | HashMap<String, Object> req = new HashMap<>(); |
| | | req.put("prompt", prompt); |
| | | req.put("chatId", chatId); |
| | | Consumer<Throwable> onError, |
| | | String scene) { |
| | | String traceId = nextTraceId(); |
| | | List<ResolvedRoute> routes = resolveRoutes(); |
| | | if (routes.isEmpty()) { |
| | | recordCall(traceId, scene, true, 1, null, false, null, 0L, req, null, "none", |
| | | new RuntimeException("未配置可用 LLM 路由"), "no_route"); |
| | | if (onError != null) onError.accept(new RuntimeException("未配置可用 LLM 路由")); |
| | | return; |
| | | } |
| | | attemptStream(routes, 0, req, onChunk, onComplete, onError, traceId, scene); |
| | | } |
| | | |
| | | Flux<String> flux = llmWebClient.post() |
| | | .uri(pythonPlatformUrl) |
| | | .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey) |
| | | .contentType(MediaType.APPLICATION_JSON) |
| | | .accept(MediaType.TEXT_EVENT_STREAM) |
| | | .bodyValue(req) |
| | | .retrieve() |
| | | .bodyToFlux(String.class) |
| | | .doOnError(ex -> log.error("调用 LLM 流式失败", ex)); |
| | | private void attemptStream(List<ResolvedRoute> routes, |
| | | int index, |
| | | ChatCompletionRequest req, |
| | | Consumer<String> onChunk, |
| | | Runnable onComplete, |
| | | Consumer<Throwable> onError, |
| | | String traceId, |
| | | String scene) { |
| | | if (index >= routes.size()) { |
| | | if (onError != null) onError.accept(new RuntimeException("LLM 路由全部失败")); |
| | | return; |
| | | } |
| | | |
| | | ResolvedRoute route = routes.get(index); |
| | | ChatCompletionRequest routeReq = applyRoute(cloneRequest(req), route, true); |
| | | long start = System.currentTimeMillis(); |
| | | StringBuilder outputBuffer = new StringBuilder(); |
| | | |
| | | AtomicBoolean doneSeen = new AtomicBoolean(false); |
| | | AtomicBoolean errorSeen = new AtomicBoolean(false); |
| | | AtomicBoolean emitted = new AtomicBoolean(false); |
| | | LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(); |
| | | |
| | | Thread drain = new Thread(() -> { |
| | |
| | | while (true) { |
| | | String s = queue.poll(2, TimeUnit.SECONDS); |
| | | if (s != null) { |
| | | emitted.set(true); |
| | | try { |
| | | onChunk.accept(s); |
| | | } catch (Exception ignore) { |
| | |
| | | } |
| | | } |
| | | } catch (InterruptedException ignore) { |
| | | ignore.printStackTrace(); |
| | | } |
| | | }); |
| | | drain.setDaemon(true); |
| | | drain.start(); |
| | | |
| | | flux.subscribe(payload -> { |
| | | streamFlux(route, routeReq).subscribe(payload -> { |
| | | if (payload == null || payload.isEmpty()) return; |
| | | String[] events = payload.split("\\r?\\n\\r?\\n"); |
| | | for (String part : events) { |
| | |
| | | doneSeen.set(true); |
| | | continue; |
| | | } |
| | | if("<think>".equals(s.trim()) || "</think>".equals(s.trim())) { |
| | | queue.offer(s.trim()); |
| | | continue; |
| | | } |
| | | try { |
| | | JSONObject obj = JSON.parseObject(s); |
| | | JSONArray choices = obj.getJSONArray("choices"); |
| | |
| | | if (delta != null) { |
| | | String content = delta.getString("content"); |
| | | if (content != null) { |
| | | try { |
| | | queue.offer(content); |
| | | } catch (Exception ignore) { |
| | | } |
| | | appendLimited(outputBuffer, content); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | log.warn("解析 LLM stream 片段失败: {}", e.getMessage()); |
| | | } |
| | | } |
| | | }, err -> { |
| | | errorSeen.set(true); |
| | | doneSeen.set(true); |
| | | boolean quota = isQuotaExhausted(err); |
| | | boolean canSwitch = shouldSwitch(route, quota); |
| | | markFailure(route, err, canSwitch); |
| | | recordCall(traceId, scene, true, index + 1, route, false, statusCodeOf(err), |
| | | System.currentTimeMillis() - start, routeReq, outputBuffer.toString(), |
| | | quota ? "quota" : "error", err, "emitted=" + emitted.get()); |
| | | if (!emitted.get() && canSwitch && index < routes.size() - 1) { |
| | | log.warn("LLM 路由失败,自动切换,current={}, reason={}", route.tag(), errorText(err)); |
| | | attemptStream(routes, index + 1, req, onChunk, onComplete, onError, traceId, scene); |
| | | return; |
| | | } |
| | | if (onError != null) onError.accept(err); |
| | | }, () -> { |
| | | if (!doneSeen.get()) { |
| | | RuntimeException ex = new RuntimeException("LLM 流意外完成"); |
| | | errorSeen.set(true); |
| | | doneSeen.set(true); |
| | | if (onError != null) onError.accept(new RuntimeException("LLM 流意外完成")); |
| | | boolean canSwitch = shouldSwitch(route, false); |
| | | markFailure(route, ex, canSwitch); |
| | | recordCall(traceId, scene, true, index + 1, route, false, 200, |
| | | System.currentTimeMillis() - start, routeReq, outputBuffer.toString(), |
| | | "error", ex, "unexpected_stream_end"); |
| | | if (!emitted.get() && canSwitch && index < routes.size() - 1) { |
| | | log.warn("LLM 路由流异常完成,自动切换,current={}", route.tag()); |
| | | attemptStream(routes, index + 1, req, onChunk, onComplete, onError, traceId, scene); |
| | | } else { |
| | | if (onError != null) onError.accept(ex); |
| | | } |
| | | } else { |
| | | markSuccess(route); |
| | | recordCall(traceId, scene, true, index + 1, route, true, 200, |
| | | System.currentTimeMillis() - start, routeReq, outputBuffer.toString(), |
| | | "none", null, null); |
| | | doneSeen.set(true); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private Flux<String> streamFlux(ResolvedRoute route, ChatCompletionRequest req) { |
| | | WebClient client = WebClient.builder().baseUrl(route.baseUrl).build(); |
| | | return client.post() |
| | | .uri("/chat/completions") |
| | | .header(HttpHeaders.AUTHORIZATION, "Bearer " + route.apiKey) |
| | | .contentType(MediaType.APPLICATION_JSON) |
| | | .accept(MediaType.TEXT_EVENT_STREAM) |
| | | .bodyValue(req) |
| | | .exchangeToFlux(resp -> { |
| | | int status = resp.rawStatusCode(); |
| | | if (status >= 200 && status < 300) { |
| | | return resp.bodyToFlux(String.class); |
| | | } |
| | | return resp.bodyToMono(String.class) |
| | | .defaultIfEmpty("") |
| | | .flatMapMany(body -> Flux.error(new LlmRouteException(status, body))); |
| | | }) |
| | | .doOnError(ex -> log.error("调用 LLM 流式失败, route={}", route.tag(), ex)); |
| | | } |
| | | |
| | | private CompletionCallResult callCompletion(ResolvedRoute route, ChatCompletionRequest req) { |
| | | WebClient client = WebClient.builder().baseUrl(route.baseUrl).build(); |
| | | RawCompletionResult raw = client.post() |
| | | .uri("/chat/completions") |
| | | .header(HttpHeaders.AUTHORIZATION, "Bearer " + route.apiKey) |
| | | .contentType(MediaType.APPLICATION_JSON) |
| | | .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_EVENT_STREAM) |
| | | .bodyValue(req) |
| | | .exchangeToMono(resp -> resp.bodyToFlux(String.class) |
| | | .collectList() |
| | | .map(list -> new RawCompletionResult(resp.rawStatusCode(), String.join("\\n\\n", list)))) |
| | | .block(); |
| | | |
| | | if (raw == null) { |
| | | throw new RuntimeException("LLM 返回为空"); |
| | | } |
| | | if (raw.statusCode < 200 || raw.statusCode >= 300) { |
| | | throw new LlmRouteException(raw.statusCode, raw.payload); |
| | | } |
| | | return new CompletionCallResult(raw.statusCode, raw.payload, parseCompletion(raw.payload)); |
| | | } |
| | | |
| | | private ChatCompletionRequest applyRoute(ChatCompletionRequest req, ResolvedRoute route, boolean stream) { |
| | | req.setModel(route.model); |
| | | req.setStream(stream); |
| | | if (route.thinkingEnabled) { |
| | | ChatCompletionRequest.Thinking t = new ChatCompletionRequest.Thinking(); |
| | | t.setType("enable"); |
| | | req.setThinking(t); |
| | | } else { |
| | | req.setThinking(null); |
| | | } |
| | | return req; |
| | | } |
| | | |
| | | private ChatCompletionRequest cloneRequest(ChatCompletionRequest src) { |
| | | ChatCompletionRequest req = new ChatCompletionRequest(); |
| | | req.setModel(src.getModel()); |
| | | req.setMessages(src.getMessages()); |
| | | req.setTemperature(src.getTemperature()); |
| | | req.setMax_tokens(src.getMax_tokens()); |
| | | req.setStream(src.getStream()); |
| | | req.setTools(src.getTools()); |
| | | req.setTool_choice(src.getTool_choice()); |
| | | req.setThinking(src.getThinking()); |
| | | return req; |
| | | } |
| | | |
| | | private boolean isValidCompletion(ChatCompletionResponse response) { |
| | | if (response == null || response.getChoices() == null || response.getChoices().isEmpty()) { |
| | | return false; |
| | | } |
| | | ChatCompletionRequest.Message message = response.getChoices().get(0).getMessage(); |
| | | if (message == null) { |
| | | return false; |
| | | } |
| | | if (!isBlank(message.getContent())) { |
| | | return true; |
| | | } |
| | | return message.getTool_calls() != null && !message.getTool_calls().isEmpty(); |
| | | } |
| | | |
| | | private boolean shouldSwitch(ResolvedRoute route, boolean quota) { |
| | | return quota ? route.switchOnQuota : route.switchOnError; |
| | | } |
| | | |
| | | private void markSuccess(ResolvedRoute route) { |
| | | if (route.id != null) { |
| | | llmRoutingService.markSuccess(route.id); |
| | | } |
| | | } |
| | | |
| | | private void markFailure(ResolvedRoute route, Throwable ex, boolean enterCooldown) { |
| | | if (route.id != null) { |
| | | llmRoutingService.markFailure(route.id, errorText(ex), enterCooldown, route.cooldownSeconds); |
| | | } |
| | | } |
| | | |
| | | private String errorText(Throwable ex) { |
| | | if (ex == null) return "unknown"; |
| | | if (ex instanceof LlmRouteException) { |
| | | LlmRouteException e = (LlmRouteException) ex; |
| | | String body = e.body == null ? "" : e.body; |
| | | if (body.length() > 240) { |
| | | body = body.substring(0, 240); |
| | | } |
| | | return "status=" + e.statusCode + ", body=" + body; |
| | | } |
| | | return ex.getMessage() == null ? ex.toString() : ex.getMessage(); |
| | | } |
| | | |
| | | private boolean isQuotaExhausted(Throwable ex) { |
| | | if (!(ex instanceof LlmRouteException)) return false; |
| | | LlmRouteException e = (LlmRouteException) ex; |
| | | if (e.statusCode == 429) return true; |
| | | String text = (e.body == null ? "" : e.body).toLowerCase(); |
| | | return text.contains("insufficient_quota") |
| | | || text.contains("quota") |
| | | || text.contains("余额") |
| | | || text.contains("用量") |
| | | || text.contains("超限") |
| | | || text.contains("rate limit"); |
| | | } |
| | | |
| | | private List<ResolvedRoute> resolveRoutes() { |
| | | List<ResolvedRoute> routes = new ArrayList<>(); |
| | | List<LlmRouteConfig> dbRoutes = llmRoutingService.listAvailableRoutes(); |
| | | for (LlmRouteConfig c : dbRoutes) { |
| | | routes.add(ResolvedRoute.fromDb(c)); |
| | | } |
| | | // 兼容:数据库为空时,回退到 yml |
| | | if (routes.isEmpty() && !isBlank(fallbackBaseUrl) && !isBlank(fallbackApiKey) && !isBlank(fallbackModel)) { |
| | | routes.add(ResolvedRoute.fromFallback(fallbackBaseUrl, fallbackApiKey, fallbackModel, isFallbackThinkingEnabled())); |
| | | } |
| | | return routes; |
| | | } |
| | | |
| | | private boolean isFallbackThinkingEnabled() { |
| | | String x = fallbackThinking == null ? "" : fallbackThinking.trim().toLowerCase(); |
| | | return "true".equals(x) || "1".equals(x) || "enable".equals(x); |
| | | } |
| | | |
| | | private boolean isBlank(String s) { |
| | | return s == null || s.trim().isEmpty(); |
| | | } |
| | | |
| | | private ChatCompletionResponse mergeSseChunk(ChatCompletionResponse acc, String payload) { |
| | |
| | | ChatCompletionResponse.Choice choice = new ChatCompletionResponse.Choice(); |
| | | ChatCompletionRequest.Message msg = new ChatCompletionRequest.Message(); |
| | | choice.setMessage(msg); |
| | | java.util.ArrayList<ChatCompletionResponse.Choice> list = new java.util.ArrayList<>(); |
| | | ArrayList<ChatCompletionResponse.Choice> list = new ArrayList<>(); |
| | | list.add(choice); |
| | | acc.setChoices(list); |
| | | } |
| | |
| | | if (created != null) acc.setCreated(created); |
| | | String object = obj.getString("object"); |
| | | if (object != null && !object.isEmpty()) acc.setObjectName(object); |
| | | } catch (Exception ignore) {} |
| | | } catch (Exception ignore) { |
| | | } |
| | | } |
| | | return acc; |
| | | } |
| | |
| | | if (r != null && r.getChoices() != null && !r.getChoices().isEmpty() && r.getChoices().get(0).getMessage() != null) { |
| | | return r; |
| | | } |
| | | } catch (Exception ignore) {} |
| | | } catch (Exception ignore) { |
| | | } |
| | | ChatCompletionResponse sse = mergeSseChunk(new ChatCompletionResponse(), payload); |
| | | if (sse.getChoices() != null && !sse.getChoices().isEmpty() && sse.getChoices().get(0).getMessage() != null && sse.getChoices().get(0).getMessage().getContent() != null) { |
| | | return sse; |
| | |
| | | msg.setRole("assistant"); |
| | | msg.setContent(payload); |
| | | choice.setMessage(msg); |
| | | java.util.ArrayList<ChatCompletionResponse.Choice> list = new java.util.ArrayList<>(); |
| | | ArrayList<ChatCompletionResponse.Choice> list = new ArrayList<>(); |
| | | list.add(choice); |
| | | r.setChoices(list); |
| | | return r; |
| | | } |
| | | |
| | | private String nextTraceId() { |
| | | return UUID.randomUUID().toString().replace("-", ""); |
| | | } |
| | | |
| | | private void appendLimited(StringBuilder sb, String text) { |
| | | if (sb == null || text == null || text.isEmpty()) { |
| | | return; |
| | | } |
| | | int remain = LOG_TEXT_LIMIT - sb.length(); |
| | | if (remain <= 0) { |
| | | return; |
| | | } |
| | | if (text.length() <= remain) { |
| | | sb.append(text); |
| | | } else { |
| | | sb.append(text, 0, remain); |
| | | } |
| | | } |
| | | |
| | | private Integer statusCodeOf(Throwable ex) { |
| | | if (ex instanceof LlmRouteException) { |
| | | return ((LlmRouteException) ex).statusCode; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private String responseBodyOf(Throwable ex) { |
| | | if (ex instanceof LlmRouteException) { |
| | | return cut(((LlmRouteException) ex).body, LOG_TEXT_LIMIT); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private String buildResponseText(ChatCompletionResponse resp, String fallbackPayload) { |
| | | if (resp != null && resp.getChoices() != null && !resp.getChoices().isEmpty() |
| | | && resp.getChoices().get(0) != null && resp.getChoices().get(0).getMessage() != null) { |
| | | ChatCompletionRequest.Message m = resp.getChoices().get(0).getMessage(); |
| | | if (!isBlank(m.getContent())) { |
| | | return cut(m.getContent(), LOG_TEXT_LIMIT); |
| | | } |
| | | if (m.getTool_calls() != null && !m.getTool_calls().isEmpty()) { |
| | | return cut(JSON.toJSONString(m), LOG_TEXT_LIMIT); |
| | | } |
| | | } |
| | | return cut(fallbackPayload, LOG_TEXT_LIMIT); |
| | | } |
| | | |
| | | private String safeName(Throwable ex) { |
| | | return ex == null ? null : ex.getClass().getSimpleName(); |
| | | } |
| | | |
| | | private String cut(String text, int maxLen) { |
| | | if (text == null) return null; |
| | | String clean = text.replace("\r", " "); |
| | | return clean.length() > maxLen ? clean.substring(0, maxLen) : clean; |
| | | } |
| | | |
| | | private void recordCall(String traceId, |
| | | String scene, |
| | | boolean stream, |
| | | int attemptNo, |
| | | ResolvedRoute route, |
| | | boolean success, |
| | | Integer httpStatus, |
| | | long latencyMs, |
| | | ChatCompletionRequest req, |
| | | String response, |
| | | String switchMode, |
| | | Throwable err, |
| | | String extra) { |
| | | LlmCallLog item = new LlmCallLog(); |
| | | item.setTraceId(cut(traceId, 64)); |
| | | item.setScene(cut(scene, 64)); |
| | | item.setStream((short) (stream ? 1 : 0)); |
| | | item.setAttemptNo(attemptNo); |
| | | if (route != null) { |
| | | item.setRouteId(route.id); |
| | | item.setRouteName(cut(route.name, 128)); |
| | | item.setBaseUrl(cut(route.baseUrl, 255)); |
| | | item.setModel(cut(route.model, 128)); |
| | | } |
| | | item.setSuccess((short) (success ? 1 : 0)); |
| | | item.setHttpStatus(httpStatus); |
| | | item.setLatencyMs(latencyMs < 0 ? 0 : latencyMs); |
| | | item.setSwitchMode(cut(switchMode, 32)); |
| | | item.setRequestContent(cut(JSON.toJSONString(req), LOG_TEXT_LIMIT)); |
| | | item.setResponseContent(cut(response, LOG_TEXT_LIMIT)); |
| | | item.setErrorType(cut(safeName(err), 128)); |
| | | item.setErrorMessage(err == null ? null : cut(errorText(err), 1024)); |
| | | item.setExtra(cut(extra, 512)); |
| | | item.setCreateTime(new Date()); |
| | | llmCallLogService.saveIgnoreError(item); |
| | | } |
| | | |
| | | private static class CompletionCallResult { |
| | | private final int statusCode; |
| | | private final String payload; |
| | | private final ChatCompletionResponse response; |
| | | |
| | | private CompletionCallResult(int statusCode, String payload, ChatCompletionResponse response) { |
| | | this.statusCode = statusCode; |
| | | this.payload = payload; |
| | | this.response = response; |
| | | } |
| | | } |
| | | |
| | | private static class RawCompletionResult { |
| | | private final int statusCode; |
| | | private final String payload; |
| | | |
| | | private RawCompletionResult(int statusCode, String payload) { |
| | | this.statusCode = statusCode; |
| | | this.payload = payload; |
| | | } |
| | | } |
| | | |
| | | private static class LlmRouteException extends RuntimeException { |
| | | private final int statusCode; |
| | | private final String body; |
| | | |
| | | private LlmRouteException(int statusCode, String body) { |
| | | super("http status=" + statusCode); |
| | | this.statusCode = statusCode; |
| | | this.body = body; |
| | | } |
| | | } |
| | | |
| | | private static class ResolvedRoute { |
| | | private Long id; |
| | | private String name; |
| | | private String baseUrl; |
| | | private String apiKey; |
| | | private String model; |
| | | private boolean thinkingEnabled; |
| | | private boolean switchOnQuota; |
| | | private boolean switchOnError; |
| | | private Integer cooldownSeconds; |
| | | |
| | | private static ResolvedRoute fromDb(LlmRouteConfig c) { |
| | | ResolvedRoute r = new ResolvedRoute(); |
| | | r.id = c.getId(); |
| | | r.name = c.getName(); |
| | | r.baseUrl = c.getBaseUrl(); |
| | | r.apiKey = c.getApiKey(); |
| | | r.model = c.getModel(); |
| | | r.thinkingEnabled = c.getThinking() != null && c.getThinking() == 1; |
| | | r.switchOnQuota = c.getSwitchOnQuota() == null || c.getSwitchOnQuota() == 1; |
| | | r.switchOnError = c.getSwitchOnError() == null || c.getSwitchOnError() == 1; |
| | | r.cooldownSeconds = c.getCooldownSeconds(); |
| | | return r; |
| | | } |
| | | |
| | | private static ResolvedRoute fromFallback(String baseUrl, String apiKey, String model, boolean thinkingEnabled) { |
| | | ResolvedRoute r = new ResolvedRoute(); |
| | | r.name = "fallback-yml"; |
| | | r.baseUrl = baseUrl; |
| | | r.apiKey = apiKey; |
| | | r.model = model; |
| | | r.thinkingEnabled = thinkingEnabled; |
| | | r.switchOnQuota = true; |
| | | r.switchOnError = true; |
| | | r.cooldownSeconds = 300; |
| | | return r; |
| | | } |
| | | |
| | | private String tag() { |
| | | String showName = name == null ? "unnamed" : name; |
| | | String showModel = model == null ? "" : (" model=" + model); |
| | | return showName + showModel; |
| | | } |
| | | } |
| | | } |
| | |
| | | @Autowired |
| | | private LlmChatService llmChatService; |
| | | |
| | | public boolean runPython(String prompt, String chatId, SseEmitter emitter) { |
| | | try { |
| | | llmChatService.chatStreamRunPython(prompt, chatId, s -> { |
| | | try { |
| | | String safe = s == null ? "" : s.replace("\r", "").replace("\n", "\\n"); |
| | | if (!safe.isEmpty()) { |
| | | sse(emitter, safe); |
| | | } |
| | | } catch (Exception ignore) { |
| | | } |
| | | }, () -> { |
| | | try { |
| | | sse(emitter, "\\n\\n【AI】运行已停止(正常结束)\\n\\n"); |
| | | log.info("AI MCP diagnose stopped: final end"); |
| | | emitter.complete(); |
| | | } catch (Exception ignore) { |
| | | } |
| | | }, e -> { |
| | | sse(emitter, "\\n\\n【AI】分析出错,正在回退...\\n\\n"); |
| | | }); |
| | | return true; |
| | | } catch (Exception e) { |
| | | try { |
| | | sse(emitter, "\\n\\n【AI】运行已停止(异常)\\n\\n"); |
| | | log.error("AI MCP diagnose stopped: error", e); |
| | | emitter.completeWithError(e); |
| | | } catch (Exception ignore) {} |
| | | return true; |
| | | } |
| | | } |
| | | // public boolean runPython(String prompt, String chatId, SseEmitter emitter) { |
| | | // try { |
| | | // llmChatService.chatStreamRunPython(prompt, chatId, s -> { |
| | | // try { |
| | | // String safe = s == null ? "" : s.replace("\r", "").replace("\n", "\\n"); |
| | | // if (!safe.isEmpty()) { |
| | | // sse(emitter, safe); |
| | | // } |
| | | // } catch (Exception ignore) { |
| | | // } |
| | | // }, () -> { |
| | | // try { |
| | | // sse(emitter, "\\n\\n【AI】运行已停止(正常结束)\\n\\n"); |
| | | // log.info("AI MCP diagnose stopped: final end"); |
| | | // emitter.complete(); |
| | | // } catch (Exception ignore) { |
| | | // } |
| | | // }, e -> { |
| | | // sse(emitter, "\\n\\n【AI】分析出错,正在回退...\\n\\n"); |
| | | // }); |
| | | // return true; |
| | | // } catch (Exception e) { |
| | | // try { |
| | | // sse(emitter, "\\n\\n【AI】运行已停止(异常)\\n\\n"); |
| | | // log.error("AI MCP diagnose stopped: error", e); |
| | | // emitter.completeWithError(e); |
| | | // } catch (Exception ignore) {} |
| | | // return true; |
| | | // } |
| | | // } |
| | | |
| | | private void sse(SseEmitter emitter, String data) { |
| | | if (data == null) return; |
| | |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | |
| | | private static final long CHAT_TTL_SECONDS = 7L * 24 * 3600; |
| | | |
| | | @Value("${llm.platform}") |
| | | private String platform; |
| | | @Autowired |
| | | private LlmChatService llmChatService; |
| | | @Autowired |
| | |
| | | private AiUtils aiUtils; |
| | | @Autowired(required = false) |
| | | private McpController mcpController; |
| | | @Autowired |
| | | private PythonService pythonService; |
| | | |
| | | public void diagnoseStream(WcsDiagnosisRequest request, SseEmitter emitter) { |
| | | List<ChatCompletionRequest.Message> messages = new ArrayList<>(); |
| | |
| | | try { |
| | | try { emitter.send(SseEmitter.event().data("【AI】运行已停止(异常)")); } catch (Exception ignore) {} |
| | | log.error("AI diagnose stream stopped: error", e); |
| | | emitter.completeWithError(e); |
| | | emitter.complete(); |
| | | } catch (Exception ignore) {} |
| | | }); |
| | | } |
| | |
| | | String chatId, |
| | | boolean reset, |
| | | SseEmitter emitter) { |
| | | if (platform.equals("python")) { |
| | | pythonService.runPython(prompt, chatId, emitter); |
| | | return; |
| | | } |
| | | |
| | | List<ChatCompletionRequest.Message> messages = new ArrayList<>(); |
| | | |
| | | List<ChatCompletionRequest.Message> history = null; |
| | |
| | | emitter.complete(); |
| | | } catch (Exception ignore) {} |
| | | }, e -> { |
| | | try { emitter.completeWithError(e); } catch (Exception ignore) {} |
| | | try { |
| | | try { emitter.send(SseEmitter.event().data("【AI】运行已停止(异常)")); } catch (Exception ignore) {} |
| | | emitter.complete(); |
| | | } catch (Exception ignore) {} |
| | | }); |
| | | } |
| | | |
| | |
| | | try { |
| | | sse(emitter, "\\n\\n【AI】运行已停止(异常)\\n\\n"); |
| | | log.error("AI MCP diagnose stopped: error", e); |
| | | emitter.completeWithError(e); |
| | | emitter.complete(); |
| | | } catch (Exception ignore) {} |
| | | return true; |
| | | } |
| | |
| | | HashMap<Integer, List<StationObjModel>> inStationMap = new HashMap<>(); |
| | | HashMap<Integer, List<StationObjModel>> outStationMap = new HashMap<>(); |
| | | HashMap<Integer, List<StationObjModel>> runBlockReassignStationMap = new HashMap<>(); |
| | | HashMap<Integer, List<StationObjModel>> isOutOrderStationMap = new HashMap<>(); |
| | | HashMap<Integer, List<StationObjModel>> isLiftTransferStationMap = new HashMap<>(); |
| | | |
| | | for (Map.Entry<Integer, List<List<HashMap<String, Object>>>> entry : dataMap.entrySet()) { |
| | | Integer lev = entry.getKey(); |
| | |
| | | HashMap<String, Object> nodeData = new HashMap<>(); |
| | | nodeData.put("value", map.get("value")); |
| | | |
| | | String nodeType = map.get("bgColor").toString(); |
| | | if (nodeType.equals("RGB(0,176,80)")) { |
| | | String bgColor = map.get("bgColor").toString(); |
| | | String nodeType = map.get("nodeType").toString(); |
| | | if (nodeType.equals("shelf")) { |
| | | //货架 |
| | | nodeData.put("type", "shelf"); |
| | | } else if (nodeType.equals("RGB(255,192,0)")) { |
| | | } else if (nodeType.equals("crn")) { |
| | | //堆垛机 |
| | | nodeData.put("type", "crn"); |
| | | } else if (nodeType.equals("RGB(255,255,0)")) { |
| | | } else if (nodeType.equals("dualCrn")) { |
| | | //双工位堆垛机 |
| | | nodeData.put("type", "dualCrn"); |
| | | } else if (nodeType.equals("RGB(0,112,192)")) { |
| | | } else if (nodeType.equals("devp")) { |
| | | //输送线 |
| | | nodeData.put("type", "devp"); |
| | | |
| | |
| | | runBlockReassignStationList.add(stationObjModel); |
| | | runBlockReassignStationMap.put(deviceNo, runBlockReassignStationList); |
| | | } |
| | | } else if (nodeType.equals("RGB(0,176,240)")) { |
| | | |
| | | Integer isOutOrder = value.getInteger("isOutOrder"); |
| | | if (isOutOrder != null && isOutOrder == 1) { |
| | | List<StationObjModel> isOutOrderStationList = isOutOrderStationMap.getOrDefault(deviceNo, new ArrayList<>()); |
| | | isOutOrderStationList.add(stationObjModel); |
| | | isOutOrderStationMap.put(deviceNo, isOutOrderStationList); |
| | | } |
| | | |
| | | Integer isLiftTransfer = value.getInteger("isLiftTransfer"); |
| | | if (isLiftTransfer != null && isLiftTransfer == 1) { |
| | | List<StationObjModel> isLiftTransferStationList = isLiftTransferStationMap.getOrDefault(deviceNo, new ArrayList<>()); |
| | | isLiftTransferStationList.add(stationObjModel); |
| | | isLiftTransferStationMap.put(deviceNo, isLiftTransferStationList); |
| | | } |
| | | } else if (nodeType.equals("rgv")) { |
| | | //RGV |
| | | nodeData.put("type", "rgv"); |
| | | } else if (nodeType.equals("none")) { |
| | |
| | | } else if (nodeType.equals("merge")) { |
| | | //合并区域 |
| | | nodeData.put("type", "merge"); |
| | | nodeData.put("mergeType", map.get("mergeType")); |
| | | } |
| | | |
| | | nodeData.put("cellWidth", map.get("cellWidth")); |
| | |
| | | List<StationObjModel> inStationList = inStationMap.get(deviceNo); |
| | | List<StationObjModel> outStationList = outStationMap.get(deviceNo); |
| | | List<StationObjModel> runBlockReassignStationList = runBlockReassignStationMap.get(deviceNo); |
| | | List<StationObjModel> isOutOrderStationList = isOutOrderStationMap.get(deviceNo); |
| | | List<StationObjModel> isLiftTransferStationList = isLiftTransferStationMap.get(deviceNo); |
| | | |
| | | if (barcodeStationList != null) { |
| | | basDevp.setBarcodeStationList(JSON.toJSONString(barcodeStationList, SerializerFeature.DisableCircularReferenceDetect)); |
| | |
| | | basDevp.setRunBlockReassignLocStationList(JSON.toJSONString(runBlockReassignStationList, SerializerFeature.DisableCircularReferenceDetect)); |
| | | } |
| | | |
| | | if (isOutOrderStationList != null) { |
| | | basDevp.setIsOutOrderList(JSON.toJSONString(isOutOrderStationList, SerializerFeature.DisableCircularReferenceDetect)); |
| | | } |
| | | |
| | | if (isLiftTransferStationList != null) { |
| | | basDevp.setIsLiftTransferList(JSON.toJSONString(isLiftTransferStationList, SerializerFeature.DisableCircularReferenceDetect)); |
| | | } |
| | | |
| | | basDevp.setStationList(JSON.toJSONString(stationList, SerializerFeature.DisableCircularReferenceDetect)); |
| | | basDevp.setUpdateTime(new Date()); |
| | | basDevpService.insertOrUpdate(basDevp); |
| | |
| | | private RedisUtil redisUtil; |
| | | @Autowired |
| | | private LocMastService locMastService; |
| | | @Autowired |
| | | private BasMapService basMapService; |
| | | @Autowired |
| | | private StationCycleCapacityService stationCycleCapacityService; |
| | | |
| | | @PostMapping("/system/running/status") |
| | | @ManagerAuth(memo = "系统运行状态") |
| | |
| | | vo.setErrorMsg(stationProtocol.getErrorMsg()); // 报警信息 |
| | | vo.setBarcode(stationProtocol.getBarcode()); // 条码 |
| | | vo.setWeight(stationProtocol.getWeight());//重量 |
| | | vo.setTaskWriteIdx(stationProtocol.getTaskWriteIdx());//任务可写区 |
| | | String stationStatus = StationStatusType.process(stationProtocol).toString().toLowerCase().replaceAll("_", "-"); |
| | | if (stationProtocol.isAutoing() && stationProtocol.isLoading() && stationProtocol.getTaskNo() > 0 && !stationProtocol.isRunBlock()) { |
| | | String taskClass = getStationTaskClass(stationProtocol.getTaskNo(), inTaskRange, outTaskRange); |
| | |
| | | return R.ok().add(vos); |
| | | } |
| | | |
| | | @GetMapping("/latest/data/station/cycle/capacity") |
| | | public R stationCycleCapacity() { |
| | | return R.ok().add(stationCycleCapacityService.getLatestSnapshot()); |
| | | } |
| | | |
| | | // @PostMapping("/latest/data/barcode") |
| | | // @ManagerAuth(memo = "条码扫描仪实时数据") |
| | | // public R barcodeLatestData(){ |
| | |
| | | @GetMapping("/map/{lev}/auth") |
| | | public R getLocMap(@PathVariable Integer lev) { |
| | | Object object = redisUtil.get(RedisKeyType.LOC_MAP_BASE.key); |
| | | if (object == null) { |
| | | return R.error("地图未初始化"); |
| | | List<List<HashMap<String, Object>>> mapNodeList = null; |
| | | if (object != null) { |
| | | mapNodeList = (List<List<HashMap<String, Object>>>) object; |
| | | } |
| | | List<List<HashMap<String, Object>>> mapNodeList = (List<List<HashMap<String, Object>>>) object; |
| | | List<LocMast> locMastList = locMastService.selectLocByLev(lev); |
| | | for (LocMast locMast : locMastList) { |
| | | String[] locType = locMast.getLocType().split("-"); |
| | |
| | | return R.error("线程不存在"); |
| | | } |
| | | |
| | | CrnCommand command = crnThread.getResetCommand(crnNo); |
| | | CrnCommand command = crnThread.getResetCommand(9999, crnNo); |
| | | MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command)); |
| | | return R.ok(); |
| | | } |
| | |
| | | import com.core.common.Cools; |
| | | import com.core.common.R; |
| | | import com.zy.asrs.domain.param.DualCrnCommandParam; |
| | | import com.zy.asrs.domain.param.DualCrnUpdateTaskNoParam; |
| | | import com.zy.asrs.domain.vo.DualCrnStateTableVo; |
| | | import com.zy.asrs.entity.BasDualCrnp; |
| | | import com.zy.asrs.service.BasDualCrnpService; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.cache.MessageQueue; |
| | | import com.zy.core.cache.SlaveConnection; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.core.enums.SlaveType; |
| | | import com.zy.core.model.Task; |
| | | import com.zy.core.model.command.DualCrnCommand; |
| | |
| | | |
| | | @Autowired |
| | | private BasDualCrnpService basDualCrnpService; |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | |
| | | @PostMapping("/dualcrn/table/crn/state") |
| | | @ManagerAuth(memo = "双工位堆垛机信息表") |
| | |
| | | } |
| | | vo.setTaskNo(p.getTaskNo()); |
| | | vo.setTaskNoTwo(p.getTaskNoTwo()); |
| | | vo.setDeviceTaskNo(p.getDeviceTaskNo()); |
| | | vo.setDeviceTaskNoTwo(p.getDeviceTaskNoTwo()); |
| | | vo.setMode(p.getModeType() == null ? "-" : p.getModeType().desc); |
| | | vo.setStatus(p.getStatusType() == null ? "-" : p.getStatusType().desc); |
| | | vo.setStatusTwo(p.getStatusTypeTwo() == null ? "-" : p.getStatusTypeTwo().desc); |
| | |
| | | vo.setForkOffsetTwo(p.getForkPosTypeTwo() == null ? "-" : p.getForkPosTypeTwo().desc); |
| | | vo.setLiftPos(p.getLiftPosType() == null ? "-" : p.getLiftPosType().desc); |
| | | vo.setWalkPos(p.getWalkPos() != null && p.getWalkPos() == 0 ? "在定位" : "不在定位"); |
| | | vo.setTaskReceive(p.getTaskReceive() != null && p.getTaskReceive() == 1 ? "接收" : "无任务"); |
| | | vo.setTaskReceiveTwo(p.getTaskReceiveTwo() != null && p.getTaskReceiveTwo() == 1 ? "接收" : "无任务"); |
| | | vo.setTaskReceive(String.valueOf(p.getTaskReceive())); |
| | | vo.setTaskReceiveTwo(String.valueOf(p.getTaskReceiveTwo())); |
| | | vo.setTaskSend(String.valueOf(p.getTaskSend())); |
| | | vo.setTaskSendTwo(String.valueOf(p.getTaskSendTwo())); |
| | | |
| | | vo.setXspeed(String.format("%.2f", p.getXSpeed())); |
| | | vo.setYspeed(String.format("%.2f", p.getYSpeed())); |
| | |
| | | if (crnThread == null) { |
| | | return R.error("线程不存在"); |
| | | } |
| | | DualCrnCommand command = crnThread.getResetCommand(crnNo, station); |
| | | DualCrnCommand command = crnThread.getResetCommand(9999, crnNo, station); |
| | | MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(3, command)); |
| | | return R.ok(); |
| | | } |
| | | |
| | | @PostMapping("/dualcrn/command/updateTaskNo") |
| | | @ManagerAuth(memo = "双工位堆垛机编辑任务号") |
| | | public R dualCrnUpdateTaskNo(@RequestBody DualCrnUpdateTaskNoParam param) { |
| | | if (Cools.isEmpty(param) || param.getCrnNo() == null || param.getStation() == null || param.getTaskNo() == null) { |
| | | return R.error("缺少参数"); |
| | | } |
| | | Integer station = param.getStation(); |
| | | if (station != 1 && station != 2) { |
| | | return R.error("工位参数错误"); |
| | | } |
| | | Integer taskNo = param.getTaskNo(); |
| | | if (taskNo < 0) { |
| | | return R.error("任务号不能小于0"); |
| | | } |
| | | Integer crnNo = param.getCrnNo(); |
| | | DualCrnThread crnThread = (DualCrnThread) SlaveConnection.get(SlaveType.DualCrn, crnNo); |
| | | if (crnThread == null) { |
| | | return R.error("线程不存在"); |
| | | } |
| | | DualCrnProtocol protocol = crnThread.getStatus(); |
| | | if (protocol == null) { |
| | | return R.error("设备状态不存在"); |
| | | } |
| | | if (station == 1) { |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_STATION1_FLAG.key + crnNo, taskNo, 60 * 60 * 24); |
| | | } else { |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_STATION2_FLAG.key + crnNo, taskNo, 60 * 60 * 24); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | } |
| | |
| | | // 获取输送站点数据 |
| | | List<StationProtocol> stationProtocols = new ArrayList<>(); |
| | | List<DeviceConfig> devpList = deviceConfigService.selectList(new EntityWrapper<DeviceConfig>() |
| | | .eq("device_type", String.valueOf(SlaveType.Devp)) |
| | | ); |
| | | .eq("device_type", String.valueOf(SlaveType.Devp))); |
| | | for (DeviceConfig device : devpList) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, device.getDeviceNo()); |
| | | if (stationThread == null) { |
| | |
| | | package com.zy.asrs.controller; |
| | | |
| | | import com.baomidou.mybatisplus.mapper.EntityWrapper; |
| | | import com.zy.asrs.domain.param.StationCommandBarcodeParam; |
| | | import com.zy.asrs.entity.BasDevp; |
| | | import com.zy.asrs.entity.DeviceConfig; |
| | | import com.zy.asrs.service.BasDevpService; |
| | | import com.zy.asrs.service.DeviceConfigService; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.core.enums.StationCommandType; |
| | | import com.zy.core.model.StationObjModel; |
| | | import com.zy.system.entity.Config; |
| | | import com.zy.system.service.ConfigService; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | |
| | | @RequestMapping("/station") |
| | | public class StationController { |
| | | |
| | | @Value("${mainProcessPlugin}") |
| | | private String mainProcessPlugin; |
| | | @Autowired |
| | | private BasDevpService basDevpService; |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | @Autowired |
| | | private ConfigService configService; |
| | | @Autowired |
| | | private DeviceConfigService deviceConfigService; |
| | | |
| | | @PostMapping("/command/move") |
| | | public R commandMove(@RequestBody StationCommandMoveParam param) { |
| | |
| | | Integer taskNo = param.getTaskNo(); |
| | | Integer targetStationId = param.getTargetStationId(); |
| | | |
| | | StationObjModel finalStation = null; |
| | | List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<BasDevp>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | List<StationObjModel> list = basDevp.getStationList$(); |
| | | for (StationObjModel entity : list) { |
| | | if(entity.getStationId().equals(stationId)){ |
| | | finalStation = entity; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if(finalStation != null){ |
| | | break; |
| | | } |
| | | } |
| | | StationObjModel finalStation = findStation(stationId); |
| | | |
| | | if(finalStation == null){ |
| | | return R.error("站点不存在"); |
| | |
| | | return R.ok(); |
| | | } |
| | | |
| | | @PostMapping("/command/barcode") |
| | | public R commandBarcode(@RequestBody StationCommandBarcodeParam param) { |
| | | if (Cools.isEmpty(param) || Cools.isEmpty(param.getStationId())) { |
| | | return R.error("缺少参数"); |
| | | } |
| | | |
| | | if (!mainProcessPlugin.contains("Fake")) { |
| | | return R.error("当前系统未启用仿真插件"); |
| | | } |
| | | |
| | | Config enableFakeConfig = configService.selectOne(new EntityWrapper<Config>().eq("code", "enableFake")); |
| | | if (enableFakeConfig == null || !"Y".equals(enableFakeConfig.getValue())) { |
| | | return R.error("当前非仿真运行模式,禁止修改条码"); |
| | | } |
| | | |
| | | Integer stationId = param.getStationId(); |
| | | StationObjModel finalStation = findStation(stationId); |
| | | if (finalStation == null) { |
| | | return R.error("站点不存在"); |
| | | } |
| | | |
| | | Integer devpNo = finalStation.getDeviceNo(); |
| | | DeviceConfig deviceConfig = deviceConfigService.selectOne(new EntityWrapper<DeviceConfig>() |
| | | .eq("device_no", devpNo) |
| | | .eq("device_type", String.valueOf(SlaveType.Devp))); |
| | | if (deviceConfig == null || deviceConfig.getFake() == null || deviceConfig.getFake() != 1) { |
| | | return R.error("当前站点设备未启用仿真,禁止修改条码"); |
| | | } |
| | | |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, devpNo); |
| | | if (stationThread == null) { |
| | | return R.error("线程不存在"); |
| | | } |
| | | |
| | | String barcode = param.getBarcode(); |
| | | if (barcode == null) { |
| | | barcode = ""; |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, 9997, stationId, stationId, 0); |
| | | command.setBarcode(barcode.trim()); |
| | | MessageQueue.offer(SlaveType.Devp, devpNo, new Task(2, command)); |
| | | return R.ok(); |
| | | } |
| | | |
| | | @PostMapping("/command/reset") |
| | | public R commandReset(@RequestBody StationCommandMoveParam param) { |
| | | if (Cools.isEmpty(param)) { |
| | |
| | | return R.ok(); |
| | | } |
| | | |
| | | private StationObjModel findStation(Integer stationId) { |
| | | if (Cools.isEmpty(stationId)) { |
| | | return null; |
| | | } |
| | | |
| | | List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<BasDevp>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | List<StationObjModel> list = basDevp.getStationList$(); |
| | | for (StationObjModel entity : list) { |
| | | if (entity.getStationId().equals(stationId)) { |
| | | return entity; |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | } |
| | |
| | | DUAL_CRN_TRANSFER_TASK_COMPLETE("dual_crn_transfer_task_complete", "双工位堆垛机移库任务执行完成"), |
| | | |
| | | STATION_OUT_TASK_RUN("station_out_task_run","输送站点出库任务运行中"), |
| | | STATION_OUT_TASK_RUN_COMPLETE("station_out_task_run_complete","输送站点出库任务运行完成"), |
| | | ; |
| | | |
| | | public String flag; |
| | |
| | | |
| | | private Integer taskNoTwo = 0; |
| | | |
| | | private Integer deviceTaskNo = 0; |
| | | |
| | | private Integer deviceTaskNoTwo = 0; |
| | | |
| | | private String status = "-"; |
| | | |
| | | private String statusTwo = "-"; |
| | |
| | | |
| | | private String taskReceiveTwo = "-"; |
| | | |
| | | private String taskSend = "-"; |
| | | |
| | | private String taskSendTwo = "-"; |
| | | |
| | | private Integer bay; |
| | | |
| | | private Integer lev; |
| | |
| | | //重量 |
| | | private Double weight; |
| | | |
| | | //任务可写区 |
| | | private Integer taskWriteIdx; |
| | | |
| | | } |
| | |
| | | @TableField("run_block_reassign_loc_station_list") |
| | | private String runBlockReassignLocStationList; |
| | | |
| | | /** |
| | | * 出库排序交互点 |
| | | */ |
| | | @ApiModelProperty(value= "出库排序交互点") |
| | | @TableField("is_out_order_list") |
| | | private String isOutOrderList; |
| | | |
| | | /** |
| | | * 顶升移栽点 |
| | | */ |
| | | @ApiModelProperty(value= "顶升移栽点") |
| | | @TableField("is_lift_transfer_list") |
| | | private String isLiftTransferList; |
| | | |
| | | public BasDevp() {} |
| | | |
| | | public BasDevp(Integer devpNo,Integer status,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo,String stationList,String barcodeStationList,String inStationList,String outStationList) { |
| | |
| | | return list; |
| | | } |
| | | |
| | | public List<StationObjModel> getOutOrderList$(){ |
| | | List<StationObjModel> list = new ArrayList<>(); |
| | | if (Cools.isEmpty(this.isOutOrderList)){ |
| | | return list; |
| | | } |
| | | |
| | | List<StationObjModel> jsonList = JSON.parseArray(this.isOutOrderList, StationObjModel.class); |
| | | for (StationObjModel json : jsonList){ |
| | | list.add(json); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | public List<Integer> getOutOrderIntList(){ |
| | | List<Integer> list = new ArrayList<>(); |
| | | if (Cools.isEmpty(this.isOutOrderList)){ |
| | | return list; |
| | | } |
| | | |
| | | List<StationObjModel> jsonList = JSON.parseArray(this.isOutOrderList, StationObjModel.class); |
| | | for (StationObjModel json : jsonList){ |
| | | list.add(json.getStationId()); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | public List<StationObjModel> getLiftTransferList$(){ |
| | | List<StationObjModel> list = new ArrayList<>(); |
| | | if (Cools.isEmpty(this.isLiftTransferList)){ |
| | | return list; |
| | | } |
| | | |
| | | List<StationObjModel> jsonList = JSON.parseArray(this.isLiftTransferList, StationObjModel.class); |
| | | for (StationObjModel json : jsonList){ |
| | | list.add(json); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | } |
| | |
| | | continue; |
| | | } |
| | | |
| | | if (!locMast.getLocSts().equals("R")) { |
| | | log.info("[workNo={}]库位状态不处于R", wrkMast.getWrkNo()); |
| | | if (!(locMast.getLocSts().equals("R") || locMast.getLocSts().equals("O"))) { |
| | | log.info("[workNo={}]库位状态不处于R or O", wrkMast.getWrkNo()); |
| | | continue; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | @Scheduled(cron = "0/1 * * * * ? ") |
| | | @Transactional |
| | | public void processOutStationRun(){ |
| | | List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().eq("wrk_sts", WrkStsType.STATION_RUN.sts)); |
| | | if (wrkMasts.isEmpty()) { |
| | | return; |
| | | } |
| | | |
| | | for (WrkMast wrkMast : wrkMasts) { |
| | | String locNo = wrkMast.getSourceLocNo(); |
| | | LocMast locMast = locMastService.queryByLoc(locNo); |
| | | if (locMast == null) { |
| | | log.info("[workNo={}]库位不存在", wrkMast.getWrkNo()); |
| | | continue; |
| | | } |
| | | |
| | | if (locMast.getLocSts().equals("O")) { |
| | | continue; |
| | | } |
| | | |
| | | if (!locMast.getLocSts().equals("R")) { |
| | | log.info("[workNo={}]库位状态不处于R", wrkMast.getWrkNo()); |
| | | continue; |
| | | } |
| | | |
| | | locMast.setLocSts("O"); |
| | | locMast.setBarcode(""); |
| | | locMast.setModiTime(new Date()); |
| | | boolean result = locMastService.updateById(locMast); |
| | | if (!result) { |
| | | log.info("[workNo={}]库位状态O更新失败", wrkMast.getWrkNo()); |
| | | continue; |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | } |
| | | |
| | | HashMap<String, Object> mapData = data.get(k - 2).get(l - 2); |
| | | mapData.put("bgColor", "merge"); |
| | | Object devp = map.get("nodeType"); |
| | | mapData.put("nodeType", "merge"); |
| | | mapData.put("mergeType", devp); |
| | | mapData.put("value", map.get("value")); |
| | | } |
| | | } |
| | |
| | | if (cell == null) { |
| | | HashMap<String, Object> map = new HashMap<>(); |
| | | map.put("bgColor", "none"); |
| | | map.put("nodeType", "none"); |
| | | map.put("cellWidth", ""); |
| | | map.put("cellHeight", ""); |
| | | map.put("value", ""); |
| | |
| | | |
| | | String bgColor = getCellBackgroundColor(cell); |
| | | map.put("bgColor", bgColor); |
| | | if(bgColor.equals("RGB(0,176,80)")) { |
| | | //货架 |
| | | map.put("nodeType", "shelf"); |
| | | } else if (bgColor.equals("RGB(255,192,0)")) { |
| | | //堆垛机 |
| | | map.put("nodeType", "crn"); |
| | | } else if (bgColor.equals("RGB(255,255,0)")) { |
| | | //双工位堆垛机 |
| | | map.put("nodeType", "dualCrn"); |
| | | } else if (bgColor.equals("RGB(0,112,192)")) { |
| | | //输送线 |
| | | map.put("nodeType", "devp"); |
| | | } else if (bgColor.equals("RGB(0,176,240)")) { |
| | | //RGV |
| | | map.put("nodeType", "rgv"); |
| | | }else { |
| | | //空白区域 |
| | | map.put("nodeType", "none"); |
| | | } |
| | | |
| | | int columnIndex = cell.getColumnIndex(); |
| | | int columnWidth = sheet.getColumnWidth(columnIndex);//获取列宽 |
| | |
| | | } else if ("/console/latest/data/dualcrn".equals(url)) { |
| | | ConsoleController consoleController = SpringUtils.getBean(ConsoleController.class); |
| | | resObj = consoleController.dualCrnLatestData(); |
| | | } else if ("/console/latest/data/station/cycle/capacity".equals(url)) { |
| | | ConsoleController consoleController = SpringUtils.getBean(ConsoleController.class); |
| | | resObj = consoleController.stationCycleCapacity(); |
| | | } else if ("/crn/table/crn/state".equals(url)) { |
| | | resObj = SpringUtils.getBean(CrnController.class).crnStateTable(); |
| | | } else if ("/rgv/table/rgv/state".equals(url)) { |
| | |
| | | private NavigateNode Father;//父节点 |
| | | private List<String> directionList;//允许行走方向 |
| | | private Boolean isInflectionPoint;//是否为拐点 |
| | | private Boolean isLiftTransferPoint;//是否为顶升移栽点 |
| | | private String direction;//行走方向 |
| | | private String nodeValue;//节点数据 |
| | | private String nodeType;//节点类型 |
| | |
| | | import com.zy.common.utils.NavigateUtils; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.News; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.core.enums.SlaveType; |
| | | import com.zy.core.enums.WrkIoType; |
| | | import com.zy.core.enums.WrkStsType; |
| | | import com.zy.core.enums.*; |
| | | import com.zy.core.model.StationObjModel; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Random; |
| | | import java.util.*; |
| | | |
| | | @Slf4j |
| | | @Service |
| | |
| | | if (null == locMast) { |
| | | throw new CoolException(param.getLocNo() + "目标库位不存在"); |
| | | } |
| | | |
| | | if (!locMast.getLocSts().equals("O")) { |
| | | throw new CoolException(locMast.getLocNo() + "目标库位不处于空库状态"); |
| | | } |
| | | News.info("任务号:{} 目标库位信息:{}",param.getTaskNo(), param.getLocNo()); |
| | | |
| | | double ioPri = 100D; |
| | | if (param.getTaskPri() != null) { |
| | |
| | | |
| | | if (findCrnResult.getCrnType().equals(SlaveType.Crn)) { |
| | | wrkMast.setCrnNo(findCrnResult.getCrnNo()); |
| | | |
| | | //缓存记录当前命令堆垛机编号 |
| | | redisUtil.set(RedisKeyType.CURRENT_CIRCLE_TASK_CRN_NO.key, crnNo, 60 * 60 * 24); |
| | | } else if (findCrnResult.getCrnType().equals(SlaveType.DualCrn)) { |
| | |
| | | } |
| | | |
| | | public FindCrnNoResult findCrnNoByLocNo(String locNo) { |
| | | if (Objects.isNull(locNo)) { |
| | | News.error("任务号为空!"); |
| | | return null; |
| | | } |
| | | List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>()); |
| | | for (BasCrnp basCrnp : basCrnps) { |
| | | List<List<Integer>> rowList = basCrnp.getControlRows$(); |
| | | for (List<Integer> rows : rowList) { |
| | | // System.out.println("库位号:" + locNo); |
| | | if(rows.contains(Utils.getRow(locNo))) { |
| | | FindCrnNoResult result = new FindCrnNoResult(); |
| | | result.setCrnNo(basCrnp.getCrnNo()); |
| | |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | import com.baomidou.mybatisplus.mapper.EntityWrapper; |
| | | import com.zy.asrs.entity.BasDevp; |
| | | import com.zy.asrs.entity.BasStation; |
| | | import com.zy.asrs.service.BasDevpService; |
| | | import com.zy.asrs.service.BasStationService; |
| | | import com.zy.core.News; |
| | | import com.zy.core.model.StationObjModel; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.mapper.EntityWrapper; |
| | | import com.core.common.SpringUtils; |
| | | import com.core.exception.CoolException; |
| | | import com.zy.common.model.NavigateNode; |
| | |
| | | |
| | | @Component |
| | | public class NavigateUtils { |
| | | |
| | | private static final String CFG_STATION_PATH_LEN_WEIGHT_PERCENT = "stationPathLenWeightPercent"; |
| | | private static final String CFG_STATION_PATH_CONG_WEIGHT_PERCENT = "stationPathCongWeightPercent"; |
| | | private static final String CFG_STATION_PATH_PASS_OTHER_OUT_STATION_WEIGHT_PERCENT = "stationPathPassOtherOutStationWeightPercent"; |
| | | private static final String CFG_STATION_PATH_PASS_OTHER_OUT_STATION_FORCE_SKIP = "stationPathPassOtherOutStationForceSkip"; |
| | | |
| | | @Autowired |
| | | private BasStationService basStationService; |
| | |
| | | News.info("[WCS Debug] 站点路径开始计算,startStationId={},endStationId={}", startStationId, endStationId); |
| | | List<List<NavigateNode>> allList = navigateSolution.allSimplePaths(stationMap, startNode, endNode, 120, 500, 300); |
| | | if (allList.isEmpty()) { |
| | | throw new CoolException("未找到该路径"); |
| | | // throw new CoolException("未找到该路径"); |
| | | return new ArrayList<>(); |
| | | } |
| | | News.info("[WCS Debug] 站点路径计算完成,耗时:{}ms", System.currentTimeMillis() - startTime); |
| | | |
| | |
| | | List<NavigateNode> fitlerList = new ArrayList<>(); |
| | | for(NavigateNode navigateNode : list){ |
| | | JSONObject valuObject = JSON.parseObject(navigateNode.getNodeValue()); |
| | | if (valuObject.containsKey("rgvCalcFlag")) { |
| | | continue; |
| | | } |
| | | if(set.add(valuObject.getInteger("stationId"))){ |
| | | fitlerList.add(navigateNode); |
| | | } |
| | |
| | | for (int i = 0; i < fitlerList.size(); i++) { |
| | | NavigateNode currentNode = fitlerList.get(i); |
| | | currentNode.setIsInflectionPoint(false); |
| | | currentNode.setIsLiftTransferPoint(false); |
| | | |
| | | try { |
| | | JSONObject valueObject = JSON.parseObject(currentNode.getNodeValue()); |
| | | if (valueObject != null) { |
| | | Object isLiftTransfer = valueObject.get("isLiftTransfer"); |
| | | if (isLiftTransfer != null) { |
| | | String isLiftTransferStr = isLiftTransfer.toString(); |
| | | if ("1".equals(isLiftTransferStr) || "true".equalsIgnoreCase(isLiftTransferStr)) { |
| | | currentNode.setIsLiftTransferPoint(true); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception ignore) {} |
| | | |
| | | NavigateNode nextNode = (i + 1 < fitlerList.size()) ? fitlerList.get(i + 1) : null; |
| | | NavigateNode prevNode = (i - 1 >= 0) ? fitlerList.get(i - 1) : null; |
| | |
| | | News.info("[WCS Debug] RGV路径开始计算,startTrackSiteNo:{},endTrackSiteNo={}", startTrackSiteNo, endTrackSiteNo); |
| | | NavigateNode res_node = navigateSolution.astarSearchJava(rgvTrackMap, startNode, endNode); |
| | | if (res_node == null) { |
| | | throw new CoolException("未找到该路径"); |
| | | // throw new CoolException("未找到该路径"); |
| | | return new ArrayList<>(); |
| | | } |
| | | News.info("[WCS Debug] RGV路径计算完成,耗时:{}ms", System.currentTimeMillis() - startTime); |
| | | |
| | |
| | | } |
| | | } catch (Exception ignore) {} |
| | | |
| | | Set<Integer> outStationIdSet = loadAllOutStationIdSet(); |
| | | |
| | | double lenWeightPercent = 50.0; |
| | | double congWeightPercent = 50.0; |
| | | double passOtherOutStationWeightPercent = 100.0; |
| | | boolean forceSkipPassOtherOutStation = false; |
| | | try { |
| | | ConfigService configService = SpringUtils.getBean(ConfigService.class); |
| | | if (configService != null) { |
| | | lenWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_LEN_WEIGHT_PERCENT, lenWeightPercent); |
| | | congWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_CONG_WEIGHT_PERCENT, congWeightPercent); |
| | | passOtherOutStationWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_PASS_OTHER_OUT_STATION_WEIGHT_PERCENT, passOtherOutStationWeightPercent); |
| | | forceSkipPassOtherOutStation = loadBooleanConfig(configService, CFG_STATION_PATH_PASS_OTHER_OUT_STATION_FORCE_SKIP, false); |
| | | } |
| | | } catch (Exception ignore) {} |
| | | |
| | | List<List<NavigateNode>> candidates = new ArrayList<>(); |
| | | List<Integer> lens = new ArrayList<>(); |
| | | List<Integer> tasksList = new ArrayList<>(); |
| | | List<Double> congs = new ArrayList<>(); |
| | | List<Integer> passOtherOutStationCounts = new ArrayList<>(); |
| | | int skippedByPassOtherOutStation = 0; |
| | | |
| | | for (List<NavigateNode> path : allList) { |
| | | if (path == null || path.isEmpty()) { |
| | |
| | | } |
| | | } |
| | | double cong = len <= 0 ? 0.0 : (double) tasks / (double) len; |
| | | int passOtherOutStationCount = countPassOtherOutStations(path, outStationIdSet); |
| | | if (forceSkipPassOtherOutStation && passOtherOutStationCount > 0) { |
| | | skippedByPassOtherOutStation++; |
| | | News.info("[WCS Debug] 站点路径候选已跳过,因经过其他出库站点,startStationId={},endStationId={},passOtherOutStationCount={}", |
| | | extractStationId(path.get(0)), |
| | | extractStationId(path.get(path.size() - 1)), |
| | | passOtherOutStationCount); |
| | | continue; |
| | | } |
| | | candidates.add(path); |
| | | lens.add(len); |
| | | tasksList.add(tasks); |
| | | congs.add(cong); |
| | | passOtherOutStationCounts.add(passOtherOutStationCount); |
| | | } |
| | | |
| | | if (candidates.isEmpty()) { |
| | | if (forceSkipPassOtherOutStation && skippedByPassOtherOutStation > 0) { |
| | | News.info("[WCS Debug] 所有站点路径候选均因经过其他出库站点被强制跳过"); |
| | | return new ArrayList<>(); |
| | | } |
| | | return allList.get(0); |
| | | } |
| | | |
| | |
| | | int maxLen = Integer.MIN_VALUE; |
| | | double minCong = Double.MAX_VALUE; |
| | | double maxCong = -Double.MAX_VALUE; |
| | | int minPassOtherOutStationCount = Integer.MAX_VALUE; |
| | | int maxPassOtherOutStationCount = Integer.MIN_VALUE; |
| | | for (int i = 0; i < candidates.size(); i++) { |
| | | int l = lens.get(i); |
| | | double c = congs.get(i); |
| | | int p = passOtherOutStationCounts.get(i); |
| | | if (l < minLen) minLen = l; |
| | | if (l > maxLen) maxLen = l; |
| | | if (c < minCong) minCong = c; |
| | | if (c > maxCong) maxCong = c; |
| | | if (p < minPassOtherOutStationCount) minPassOtherOutStationCount = p; |
| | | if (p > maxPassOtherOutStationCount) maxPassOtherOutStationCount = p; |
| | | } |
| | | |
| | | //长度权重百分比 |
| | | double lenWeightPercent = 50.0; |
| | | //拥堵权重百分比 |
| | | double congWeightPercent = 50.0; |
| | | try { |
| | | ConfigService configService = SpringUtils.getBean(ConfigService.class); |
| | | if (configService != null) { |
| | | Config cfgLen = configService.selectOne(new EntityWrapper<Config>().eq("code", "stationPathLenWeightPercent")); |
| | | if (cfgLen != null && cfgLen.getValue() != null) { |
| | | String v = cfgLen.getValue().trim(); |
| | | if (v.endsWith("%")) v = v.substring(0, v.length() - 1); |
| | | try { lenWeightPercent = Double.parseDouble(v); } catch (Exception ignore) {} |
| | | } |
| | | Config cfgCong = configService.selectOne(new EntityWrapper<Config>().eq("code", "stationPathCongWeightPercent")); |
| | | if (cfgCong != null && cfgCong.getValue() != null) { |
| | | String v = cfgCong.getValue().trim(); |
| | | if (v.endsWith("%")) v = v.substring(0, v.length() - 1); |
| | | try { congWeightPercent = Double.parseDouble(v); } catch (Exception ignore) {} |
| | | } |
| | | } |
| | | } catch (Exception ignore) {} |
| | | |
| | | double weightSum = lenWeightPercent + congWeightPercent; |
| | | double weightSum = lenWeightPercent + congWeightPercent + passOtherOutStationWeightPercent; |
| | | double lenW = weightSum <= 0 ? 0.5 : lenWeightPercent / weightSum; |
| | | double congW = weightSum <= 0 ? 0.5 : congWeightPercent / weightSum; |
| | | double passOtherOutStationW = weightSum <= 0 ? 0.0 : passOtherOutStationWeightPercent / weightSum; |
| | | |
| | | List<NavigateNode> best = null; |
| | | double bestCost = Double.MAX_VALUE; |
| | | int bestPassOtherOutStationCount = Integer.MAX_VALUE; |
| | | int bestTasks = Integer.MAX_VALUE; |
| | | int bestLen = Integer.MAX_VALUE; |
| | | for (int i = 0; i < candidates.size(); i++) { |
| | | int l = lens.get(i); |
| | | int t = tasksList.get(i); |
| | | double c = congs.get(i); |
| | | int p = passOtherOutStationCounts.get(i); |
| | | //归一化 |
| | | double lenNorm = (maxLen - minLen) <= 0 ? 0.0 : (l - minLen) / (double) (maxLen - minLen); |
| | | double congNorm = (maxCong - minCong) <= 0 ? 0.0 : (c - minCong) / (double) (maxCong - minCong); |
| | | double passOtherOutStationNorm = (maxPassOtherOutStationCount - minPassOtherOutStationCount) <= 0 |
| | | ? 0.0 |
| | | : (p - minPassOtherOutStationCount) / (double) (maxPassOtherOutStationCount - minPassOtherOutStationCount); |
| | | //获取权重 |
| | | double cost = lenNorm * lenW + congNorm * congW; |
| | | double cost = lenNorm * lenW + congNorm * congW + passOtherOutStationNorm * passOtherOutStationW; |
| | | if (cost < bestCost |
| | | || (cost == bestCost && t < bestTasks) |
| | | || (cost == bestCost && t == bestTasks && l < bestLen)) { |
| | | || (cost == bestCost && p < bestPassOtherOutStationCount) |
| | | || (cost == bestCost && p == bestPassOtherOutStationCount && t < bestTasks) |
| | | || (cost == bestCost && p == bestPassOtherOutStationCount && t == bestTasks && l < bestLen)) { |
| | | best = candidates.get(i); |
| | | bestCost = cost; |
| | | bestPassOtherOutStationCount = p; |
| | | bestTasks = t; |
| | | bestLen = l; |
| | | } |
| | |
| | | return best; |
| | | } |
| | | |
| | | private Set<Integer> loadAllOutStationIdSet() { |
| | | Set<Integer> outStationIdSet = new HashSet<>(); |
| | | try { |
| | | BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class); |
| | | if (basDevpService == null) { |
| | | return outStationIdSet; |
| | | } |
| | | List<BasDevp> basDevpList = basDevpService.selectList(new EntityWrapper<BasDevp>().eq("status", 1)); |
| | | for (BasDevp basDevp : basDevpList) { |
| | | List<StationObjModel> outStationList = basDevp.getOutStationList$(); |
| | | for (StationObjModel stationObjModel : outStationList) { |
| | | if (stationObjModel != null && stationObjModel.getStationId() != null) { |
| | | outStationIdSet.add(stationObjModel.getStationId()); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception ignore) {} |
| | | return outStationIdSet; |
| | | } |
| | | |
| | | private int countPassOtherOutStations(List<NavigateNode> path, Set<Integer> outStationIdSet) { |
| | | if (path == null || path.size() < 3 || outStationIdSet == null || outStationIdSet.isEmpty()) { |
| | | return 0; |
| | | } |
| | | Integer startStationId = extractStationId(path.get(0)); |
| | | Integer endStationId = extractStationId(path.get(path.size() - 1)); |
| | | Set<Integer> hitStationIdSet = new HashSet<>(); |
| | | for (int i = 1; i < path.size() - 1; i++) { |
| | | Integer stationId = extractStationId(path.get(i)); |
| | | if (stationId == null) { |
| | | continue; |
| | | } |
| | | if (startStationId != null && startStationId.equals(stationId)) { |
| | | continue; |
| | | } |
| | | if (endStationId != null && endStationId.equals(stationId)) { |
| | | continue; |
| | | } |
| | | if (outStationIdSet.contains(stationId)) { |
| | | hitStationIdSet.add(stationId); |
| | | } |
| | | } |
| | | return hitStationIdSet.size(); |
| | | } |
| | | |
| | | private Integer extractStationId(NavigateNode node) { |
| | | if (node == null || node.getNodeValue() == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | JSONObject value = JSON.parseObject(node.getNodeValue()); |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | return value.getInteger("stationId"); |
| | | } catch (Exception ignore) {} |
| | | return null; |
| | | } |
| | | |
| | | private double loadDoubleConfig(ConfigService configService, String code, double defaultValue) { |
| | | if (configService == null || code == null) { |
| | | return defaultValue; |
| | | } |
| | | Config config = configService.selectOne(new EntityWrapper<Config>().eq("code", code)); |
| | | if (config == null || config.getValue() == null) { |
| | | return defaultValue; |
| | | } |
| | | String value = config.getValue().trim(); |
| | | if (value.endsWith("%")) { |
| | | value = value.substring(0, value.length() - 1); |
| | | } |
| | | try { |
| | | return Double.parseDouble(value); |
| | | } catch (Exception ignore) {} |
| | | return defaultValue; |
| | | } |
| | | |
| | | private boolean loadBooleanConfig(ConfigService configService, String code, boolean defaultValue) { |
| | | if (configService == null || code == null) { |
| | | return defaultValue; |
| | | } |
| | | Config config = configService.selectOne(new EntityWrapper<Config>().eq("code", code)); |
| | | if (config == null || config.getValue() == null) { |
| | | return defaultValue; |
| | | } |
| | | String value = config.getValue().trim(); |
| | | if (value.isEmpty()) { |
| | | return defaultValue; |
| | | } |
| | | return "1".equals(value) |
| | | || "true".equalsIgnoreCase(value) |
| | | || "yes".equalsIgnoreCase(value) |
| | | || "y".equalsIgnoreCase(value) |
| | | || "on".equalsIgnoreCase(value); |
| | | } |
| | | |
| | | //判断当前节点到下一个节点是否为拐点 |
| | | public HashMap<String,Object> searchInflectionPoint(NavigateNode currentNode, NavigateNode fatherNode, NavigateNode nextNode) { |
| | | HashMap<String, Object> map = new HashMap<>(); |
| | |
| | | thread = new ZyStationThread(deviceConfig, redisUtil); |
| | | } else if (deviceConfig.getThreadImpl().equals("ZyStationV3Thread")) { |
| | | thread = new ZyStationV3Thread(deviceConfig, redisUtil); |
| | | } else if (deviceConfig.getThreadImpl().equals("ZyStationV4Thread")) { |
| | | thread = new ZyStationV4Thread(deviceConfig, redisUtil); |
| | | } else { |
| | | throw new CoolException("未知的线程实现"); |
| | | } |
| | |
| | | |
| | | public enum RedisKeyType { |
| | | |
| | | SHUTTLE_WORK_FLAG("shuttle_wrk_no_"), |
| | | SHUTTLE_FLAG("shuttle_"), |
| | | FORK_LIFT_WORK_FLAG("fork_lift_wrk_no_"), |
| | | FORK_LIFT_FLAG("fork_lift_"), |
| | | LIFT_WORK_FLAG("lift_wrk_no_"), |
| | | LIFT_FLAG("lift_"), |
| | | |
| | | DUAL_CRN_STATION1_FLAG("dual_crn_station1_flag_"), |
| | | DUAL_CRN_STATION2_FLAG("dual_crn_station2_flag_"), |
| | | DUAL_CRN_COMMAND_("dual_crn_command_"), |
| | | DUAL_CRN_COMMAND_IDX("dual_crn_command_idx_"), |
| | | DUAL_CRN_COMMAND_LAST("dual_crn_command_LAST_"), |
| | | |
| | | QUEUE_CRN("queue_crn_"), |
| | | QUEUE_DUAL_CRN("queue_dual_crn_"), |
| | |
| | | DUAL_CRN_OUT_TASK_COMPLETE_STATION_INFO("dual_crn_out_task_complete_station_info_"), |
| | | CRN_OUT_TASK_COMPLETE_STATION_INFO("crn_out_task_complete_station_info_"), |
| | | |
| | | WATCH_CIRCLE_STATION_("watch_circle_station_"), |
| | | STATION_CYCLE_LOAD_RESERVE("station_cycle_load_reserve"), |
| | | |
| | | CURRENT_CIRCLE_TASK_CRN_NO("current_circle_task_crn_no_"), |
| | | ASYNC_WMS_IN_TASK_REQUEST("async_wms_in_task_request_"), |
| | | ASYNC_WMS_IN_TASK_RESPONSE("async_wms_in_task_response_"), |
| | |
| | | OUTBOUND_RUN(102, "设备搬运中"), |
| | | OUTBOUND_RUN_COMPLETE(103, "设备搬运完成"), |
| | | STATION_RUN(104, "站点运行中"), |
| | | STATION_RUN_COMPLETE(105, "站点运行完成"), |
| | | COMPLETE_OUTBOUND(109, "出库完成"), |
| | | SETTLE_OUTBOUND(110, "出库库存更新"), |
| | | |
| | |
| | | |
| | | private List<Integer> navigatePath; |
| | | |
| | | // 路径中的顶升移栽点(按路径顺序) |
| | | private List<Integer> liftTransferPath; |
| | | |
| | | private List<Integer> originalNavigatePath; |
| | | |
| | | private StationCommandType commandType; |
| | | |
| | | // 仿真模式下可选:手工指定条码 |
| | | private String barcode; |
| | | |
| | | } |
| | |
| | | package com.zy.core.model.protocol; |
| | | |
| | | import com.zy.core.enums.DualCrnForkPosType; |
| | | import com.zy.core.enums.DualCrnLiftPosType; |
| | | import com.zy.core.enums.DualCrnModeType; |
| | | import com.zy.core.enums.DualCrnStatusType; |
| | | import com.core.common.Cools; |
| | | import com.core.common.SpringUtils; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.enums.*; |
| | | |
| | | import lombok.Data; |
| | | |
| | |
| | | @Data |
| | | public class DualCrnProtocol { |
| | | |
| | | private Integer crnNo; |
| | | private volatile Integer crnNo; |
| | | |
| | | /** |
| | | * 1 = 手动模式 |
| | | * 2 = 自动模式 |
| | | * 3 = 电脑模式 |
| | | */ |
| | | public Integer mode; |
| | | public volatile Integer mode; |
| | | |
| | | public DualCrnModeType modeType; |
| | | public volatile DualCrnModeType modeType; |
| | | |
| | | /** |
| | | * 异常码 |
| | | */ |
| | | public Integer alarm; |
| | | public volatile Integer alarm; |
| | | |
| | | /** |
| | | * 工位1任务号 |
| | | * WCS工位1任务号 |
| | | */ |
| | | public Integer taskNo = 0; |
| | | public volatile Integer taskNo = 0; |
| | | |
| | | /** |
| | | * 工位2任务号 |
| | | * WCS工位2任务号 |
| | | */ |
| | | public Integer taskNoTwo = 0; |
| | | public volatile Integer taskNoTwo = 0; |
| | | |
| | | /** |
| | | * 设备工位1任务号 |
| | | */ |
| | | public volatile Integer deviceTaskNo = 0; |
| | | |
| | | /** |
| | | * 设备工位2任务号 |
| | | */ |
| | | public volatile Integer deviceTaskNoTwo = 0; |
| | | |
| | | /** |
| | | * 工位1当前状态 |
| | |
| | | * 90:任务完成等待WCS确认 |
| | | * 99:报警 |
| | | */ |
| | | public Integer status; |
| | | public volatile Integer status; |
| | | |
| | | /** |
| | | * 工位2当前状态 |
| | | */ |
| | | public Integer statusTwo; |
| | | public volatile Integer statusTwo; |
| | | |
| | | /** |
| | | * 工位1状态枚举 |
| | | */ |
| | | public DualCrnStatusType statusType; |
| | | public volatile DualCrnStatusType statusType; |
| | | |
| | | /** |
| | | * 工位2状态枚举 |
| | | */ |
| | | public DualCrnStatusType statusTypeTwo; |
| | | public volatile DualCrnStatusType statusTypeTwo; |
| | | |
| | | /** |
| | | * 工位1堆垛机当前列号 |
| | | */ |
| | | public Integer bay; |
| | | public volatile Integer bay; |
| | | |
| | | /** |
| | | * 工位2堆垛机当前列号 |
| | | */ |
| | | public Integer bayTwo; |
| | | public volatile Integer bayTwo; |
| | | |
| | | /** |
| | | * 工位1堆垛机当前层号 |
| | | */ |
| | | public Integer level; |
| | | public volatile Integer level; |
| | | |
| | | /** |
| | | * 工位2堆垛机当前层号 |
| | | */ |
| | | public Integer levelTwo; |
| | | public volatile Integer levelTwo; |
| | | |
| | | /** |
| | | * 工位1当前货叉位置 |
| | |
| | | * 1 = 货叉在左侧 |
| | | * 2 = 货叉在右侧 |
| | | */ |
| | | public Integer forkPos; |
| | | public volatile Integer forkPos; |
| | | |
| | | /** |
| | | * 工位2当前货叉位置 |
| | |
| | | * 1 = 货叉在左侧 |
| | | * 2 = 货叉在右侧 |
| | | */ |
| | | public Integer forkPosTwo; |
| | | public volatile Integer forkPosTwo; |
| | | |
| | | public DualCrnForkPosType forkPosType; |
| | | public volatile DualCrnForkPosType forkPosType; |
| | | |
| | | public DualCrnForkPosType forkPosTypeTwo; |
| | | public volatile DualCrnForkPosType forkPosTypeTwo; |
| | | |
| | | /** |
| | | * 当前载货台位置 |
| | | * 0 = 下定位 |
| | | * 1 = 上定位 |
| | | */ |
| | | public Integer liftPos; |
| | | public volatile Integer liftPos; |
| | | |
| | | public Integer liftPosTwo; |
| | | public volatile Integer liftPosTwo; |
| | | |
| | | public DualCrnLiftPosType liftPosType; |
| | | public volatile DualCrnLiftPosType liftPosType; |
| | | |
| | | public DualCrnLiftPosType liftPosTypeTwo; |
| | | public volatile DualCrnLiftPosType liftPosTypeTwo; |
| | | |
| | | /** |
| | | * 走行在定位 |
| | | * 0 = 在定位 |
| | | * 1 = 不在定位 |
| | | */ |
| | | public Integer walkPos; |
| | | public volatile Integer walkPos; |
| | | |
| | | public Integer walkPosTwo; |
| | | public volatile Integer walkPosTwo; |
| | | |
| | | /** |
| | | * 载货台有物 |
| | | */ |
| | | public Integer loaded; |
| | | public volatile Integer loaded; |
| | | |
| | | public Integer loadedTwo; |
| | | public volatile Integer loadedTwo; |
| | | |
| | | /** |
| | | * 任务接收状态 |
| | | * 0 = 未接收 |
| | | * 1 = 已接收 |
| | | */ |
| | | public Integer taskReceive; |
| | | public volatile Integer taskReceive; |
| | | |
| | | public Integer taskReceiveTwo; |
| | | public volatile Integer taskReceiveTwo; |
| | | |
| | | private Integer temp1; |
| | | public volatile Integer taskSend; |
| | | |
| | | public volatile Integer taskSendTwo; |
| | | |
| | | private volatile Integer temp1; |
| | | |
| | | private Integer temp2; |
| | | |
| | |
| | | this.statusTwo = DualCrnStatusType.get(type).id; |
| | | } |
| | | |
| | | public Integer getTaskNo() { |
| | | RedisUtil redisUtil = null; |
| | | try { |
| | | redisUtil = SpringUtils.getBean(RedisUtil.class); |
| | | }catch (Exception e) {} |
| | | if (null != redisUtil) { |
| | | Object o = redisUtil.get(RedisKeyType.DUAL_CRN_STATION1_FLAG.key + this.crnNo); |
| | | if (!Cools.isEmpty(o)) { |
| | | this.taskNo = Integer.parseInt(String.valueOf(o)); |
| | | } |
| | | } |
| | | return this.taskNo == null ? 0 : this.taskNo; |
| | | } |
| | | |
| | | public Integer getTaskNoTwo() { |
| | | RedisUtil redisUtil = null; |
| | | try { |
| | | redisUtil = SpringUtils.getBean(RedisUtil.class); |
| | | }catch (Exception e) {} |
| | | if (null != redisUtil) { |
| | | Object o = redisUtil.get(RedisKeyType.DUAL_CRN_STATION2_FLAG.key + this.crnNo); |
| | | if (!Cools.isEmpty(o)) { |
| | | this.taskNoTwo = Integer.parseInt(String.valueOf(o)); |
| | | } |
| | | } |
| | | return this.taskNoTwo == null ? 0 : this.taskNoTwo; |
| | | } |
| | | |
| | | } |
| | |
| | | //WCS系统报警 |
| | | private String systemWarning; |
| | | |
| | | //任务可写区 |
| | | private Integer taskWriteIdx; |
| | | |
| | | /** |
| | | * 扩展数据 |
| | | */ |
| | |
| | | import java.util.List; |
| | | import com.zy.core.network.fake.ZyStationFakeConnect; |
| | | import com.zy.core.network.fake.ZyStationFakeSegConnect; |
| | | import com.zy.core.network.fake.ZyStationV4FakeSegConnect; |
| | | import com.zy.core.network.real.ZyStationRealConnect; |
| | | import com.zy.core.network.real.ZyStationV3RealConnect; |
| | | import com.zy.core.network.real.ZyStationV4RealConnect; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import java.util.concurrent.Executors; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | |
| | | |
| | | private static final ZyStationFakeConnect zyStationFakeConnect = new ZyStationFakeConnect(); |
| | | private static final ZyStationFakeSegConnect zyStationFakeSegConnect = new ZyStationFakeSegConnect(); |
| | | private static final ZyStationV4FakeSegConnect zyStationV4FakeSegConnect = new ZyStationV4FakeSegConnect(); |
| | | |
| | | private boolean connected = false; |
| | | private DeviceConfig deviceConfig; |
| | |
| | | if (deviceConfig.getFake() == 0) { |
| | | if ("ZyStationV3Thread".equals(deviceConfig.getThreadImpl())) { |
| | | zyStationConnectApi = new ZyStationV3RealConnect(deviceConfig, redisUtil); |
| | | } else if ("ZyStationV4Thread".equals(deviceConfig.getThreadImpl())) { |
| | | zyStationConnectApi = new ZyStationV4RealConnect(deviceConfig, redisUtil); |
| | | } else { |
| | | zyStationConnectApi = new ZyStationRealConnect(deviceConfig, redisUtil); |
| | | } |
| | |
| | | if ("ZyStationV3Thread".equals(deviceConfig.getThreadImpl())) { |
| | | zyStationFakeSegConnect.addFakeConnect(deviceConfig, redisUtil); |
| | | zyStationConnectApi = zyStationFakeSegConnect; |
| | | } else if ("ZyStationV4Thread".equals(deviceConfig.getThreadImpl())) { |
| | | zyStationV4FakeSegConnect.addFakeConnect(deviceConfig, redisUtil); |
| | | zyStationConnectApi = zyStationV4FakeSegConnect; |
| | | } else { |
| | | zyStationFakeConnect.addFakeConnect(deviceConfig, redisUtil); |
| | | zyStationConnectApi = zyStationFakeConnect; |
| | |
| | | |
| | | public Integer taskReceiveTwo; |
| | | |
| | | public Integer taskSend; |
| | | |
| | | public Integer taskSendTwo; |
| | | |
| | | /** |
| | | * 堆垛机当前列号 |
| | | */ |
| | |
| | | //重量 |
| | | private Double weight; |
| | | |
| | | //任务可写区 |
| | | private Integer taskWriteIdx; |
| | | |
| | | //运行堵塞 |
| | | private boolean runBlock = false; |
| | | |
| | |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.zy.asrs.entity.DeviceConfig; |
| | | import com.zy.core.enums.CrnStatusType; |
| | | import com.zy.core.enums.DualCrnStatusType; |
| | | import com.zy.core.enums.DualCrnTaskModeType; |
| | | import com.zy.core.model.CommandResponse; |
| | | import com.zy.core.model.command.DualCrnCommand; |
| | |
| | | private final ExecutorService executor = Executors |
| | | .newFixedThreadPool(9999); |
| | | private int taskExecuteStation = 0; |
| | | private DualCrnCommand station1LastCommand = null; |
| | | private DualCrnCommand station2LastCommand = null; |
| | | |
| | | public ZyDualCrnFakeConnect(DeviceConfig deviceConfig) { |
| | | this.deviceConfig = deviceConfig; |
| | |
| | | this.crnStatus.setTaskNo(0); |
| | | } |
| | | this.crnStatus.setTaskReceive(0); |
| | | this.crnStatus.setStatus(CrnStatusType.IDLE.id); |
| | | if (station1LastCommand == null) { |
| | | this.crnStatus.setStatus(DualCrnStatusType.IDLE.id); |
| | | }else { |
| | | if (station1LastCommand.getTaskMode().intValue() == DualCrnTaskModeType.PICK.id) { |
| | | this.crnStatus.setStatus(DualCrnStatusType.FETCH_COMPLETE.id); |
| | | }else { |
| | | this.crnStatus.setStatus(DualCrnStatusType.IDLE.id); |
| | | } |
| | | } |
| | | }else { |
| | | if (crnStatus.getLoadedTwo() == 0) { |
| | | this.crnStatus.setTaskNoTwo(0); |
| | | } |
| | | this.crnStatus.setTaskReceiveTwo(0); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.IDLE.id); |
| | | if (station2LastCommand == null) { |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.IDLE.id); |
| | | }else { |
| | | if (station1LastCommand.getTaskMode().intValue() == DualCrnTaskModeType.PICK.id) { |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.FETCH_COMPLETE.id); |
| | | }else { |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.IDLE.id); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | if(command.getStation() == 1) { |
| | | this.crnStatus.setTaskNo(taskNo); |
| | | this.crnStatus.setStatus(CrnStatusType.MOVING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.PUT_MOVING.id); |
| | | this.crnStatus.setTaskReceive(1); |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY, this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatus(CrnStatusType.WAITING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.WAITING.id); |
| | | }else { |
| | | this.crnStatus.setTaskNoTwo(taskNo); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.MOVING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.PUT_MOVING.id); |
| | | this.crnStatus.setTaskReceive(1); |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY, this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.WAITING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.WAITING.id); |
| | | } |
| | | |
| | | taskExecuteStation = 0; |
| | |
| | | |
| | | if(command.getStation() == 1) { |
| | | this.crnStatus.setTaskNo(taskNo); |
| | | this.crnStatus.setStatus(CrnStatusType.FETCH_MOVING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.FETCH_MOVING.id); |
| | | this.crnStatus.setTaskReceive(1); |
| | | |
| | | moveYZ(this.crnStatus.getBay(), sourcePosY,this.crnStatus.getLevel(), sourcePosZ,command.getStation()); |
| | | this.crnStatus.setStatus(CrnStatusType.FETCHING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.FETCHING.id); |
| | | sleep(2000); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | | |
| | | this.crnStatus.setLoaded(1); |
| | | this.crnStatus.setStatus(CrnStatusType.PUT_MOVING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.PUT_MOVING.id); |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatus(CrnStatusType.PUTTING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.PUTTING.id); |
| | | sleep(2000); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | | this.crnStatus.setLoaded(0); |
| | | this.crnStatus.setStatus(CrnStatusType.WAITING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.WAITING.id); |
| | | |
| | | this.station1LastCommand = command; |
| | | }else { |
| | | this.crnStatus.setTaskNoTwo(taskNo); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.FETCH_MOVING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.FETCH_MOVING.id); |
| | | this.crnStatus.setTaskReceiveTwo(1); |
| | | |
| | | moveYZ(this.crnStatus.getBay(), sourcePosY,this.crnStatus.getLevel(), sourcePosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.FETCHING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.FETCHING.id); |
| | | sleep(2000); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | | |
| | | this.crnStatus.setLoadedTwo(1); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.PUT_MOVING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.PUT_MOVING.id); |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.PUTTING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.PUTTING.id); |
| | | sleep(2000); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | | this.crnStatus.setLoadedTwo(0); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.WAITING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.WAITING.id); |
| | | |
| | | this.station2LastCommand = command; |
| | | } |
| | | |
| | | taskExecuteStation = 0; |
| | |
| | | |
| | | if(command.getStation() == 1) { |
| | | this.crnStatus.setTaskNo(taskNo); |
| | | this.crnStatus.setStatus(CrnStatusType.FETCH_MOVING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.FETCH_MOVING.id); |
| | | this.crnStatus.setTaskReceive(1); |
| | | |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatus(CrnStatusType.FETCHING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.FETCHING.id); |
| | | sleep(3000); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | | this.crnStatus.setLoaded(1); |
| | | this.crnStatus.setStatus(CrnStatusType.WAITING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.WAITING.id); |
| | | |
| | | this.station1LastCommand = command; |
| | | }else { |
| | | this.crnStatus.setTaskNoTwo(taskNo); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.FETCH_MOVING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.FETCH_MOVING.id); |
| | | this.crnStatus.setTaskReceiveTwo(1); |
| | | |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.FETCHING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.FETCHING.id); |
| | | sleep(3000); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | | this.crnStatus.setLoadedTwo(1); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.WAITING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.WAITING.id); |
| | | |
| | | this.station2LastCommand = command; |
| | | } |
| | | |
| | | taskExecuteStation = 0; |
| | |
| | | |
| | | if(command.getStation() == 1) { |
| | | this.crnStatus.setTaskNo(taskNo); |
| | | this.crnStatus.setStatus(CrnStatusType.PUT_MOVING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.PUT_MOVING.id); |
| | | this.crnStatus.setTaskReceive(1); |
| | | |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatus(CrnStatusType.PUTTING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.PUTTING.id); |
| | | sleep(3000); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | | this.crnStatus.setLoaded(0); |
| | | this.crnStatus.setStatus(CrnStatusType.WAITING.id); |
| | | this.crnStatus.setStatus(DualCrnStatusType.WAITING.id); |
| | | |
| | | this.station1LastCommand = command; |
| | | }else { |
| | | this.crnStatus.setTaskNoTwo(taskNo); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.PUT_MOVING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.PUT_MOVING.id); |
| | | this.crnStatus.setTaskReceiveTwo(1); |
| | | |
| | | moveYZ(this.crnStatus.getBay(), destinationPosY,this.crnStatus.getLevel(), destinationPosZ, command.getStation()); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.PUTTING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.PUTTING.id); |
| | | sleep(3000); |
| | | if (Thread.currentThread().isInterrupted()) { |
| | | return; |
| | | } |
| | | this.crnStatus.setLoadedTwo(0); |
| | | this.crnStatus.setStatusTwo(CrnStatusType.WAITING.id); |
| | | this.crnStatus.setStatusTwo(DualCrnStatusType.WAITING.id); |
| | | |
| | | this.station2LastCommand = command; |
| | | } |
| | | |
| | | taskExecuteStation = 0; |
| | |
| | | } |
| | | |
| | | if(commandType == StationCommandType.WRITE_INFO){ |
| | | if (command.getBarcode() != null) { |
| | | updateStationBarcode(deviceNo, stationId, command.getBarcode()); |
| | | return; |
| | | } |
| | | if (taskNo == 9998 && targetStationId == 0) { |
| | | //生成出库站点仿真数据 |
| | | generateFakeOutStationData(deviceNo, stationId); |
| | |
| | | } |
| | | } |
| | | |
| | | 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 void currentLevCommand(StationCommand command, boolean generateBarcode) { |
| | | NavigateUtils navigateUtils = SpringUtils.getBean(NavigateUtils.class); |
| | | if (navigateUtils == null) { |
| | |
| | | if (command != null) { |
| | | taskLastUpdateTime.put(taskNo, System.currentTimeMillis()); |
| | | |
| | | // 首次接收命令时初始化 |
| | | if (finalTargetStationId == null) { |
| | | finalTargetStationId = command.getTargetStaNo(); |
| | | if (checkTaskNoInArea(taskNo)) { |
| | | generateBarcode = true; |
| | | // 每次接收命令都刷新目标,避免沿用旧目标导致状态抖动 |
| | | Integer commandTargetStationId = command.getTargetStaNo(); |
| | | if (commandTargetStationId != null) { |
| | | if (!commandTargetStationId.equals(finalTargetStationId)) { |
| | | News.info("[WCS Debug] 任务{}切换目标: {} -> {}", taskNo, finalTargetStationId, |
| | | commandTargetStationId); |
| | | } |
| | | finalTargetStationId = commandTargetStationId; |
| | | // 当前站点先同步最新目标,避免上层在窗口期重复下发同一路径 |
| | | syncCurrentStationTarget(taskNo, currentStationId, finalTargetStationId); |
| | | } |
| | | |
| | | if (!generateBarcode && checkTaskNoInArea(taskNo)) { |
| | | generateBarcode = true; |
| | | } |
| | | |
| | | // 将新路径追加到待执行队列 |
| | |
| | | if (newPath != null && !newPath.isEmpty()) { |
| | | // 获取队列中最后一个站点(用于衔接点去重) |
| | | Integer lastInQueue = getLastInQueue(pendingPathQueue); |
| | | if (lastInQueue == null) { |
| | | lastInQueue = currentStationId; |
| | | } |
| | | |
| | | int startIndex = 0; |
| | | // 如果新路径的起点与当前位置或队列末尾重复,则跳过 |
| | | if (lastInQueue != null && !newPath.isEmpty() && newPath.get(0).equals(lastInQueue)) { |
| | | startIndex = 1; |
| | | } |
| | | int startIndex = getPathAppendStartIndex(newPath, currentStationId, lastInQueue); |
| | | |
| | | for (int i = startIndex; i < newPath.size(); i++) { |
| | | pendingPathQueue.offer(newPath.get(i)); |
| | |
| | | // 路径队列为空,等待新的分段命令 |
| | | if (currentStationId != null && finalTargetStationId != null |
| | | && currentStationId.equals(finalTargetStationId)) { |
| | | // 已到达最终目标,正常结束 |
| | | // 已到达目标:立即清空当前队列并结束本轮执行,后续新命令重新创建执行线程 |
| | | if (generateBarcode) { |
| | | Integer targetDeviceNo = getDeviceNoByStationId(finalTargetStationId); |
| | | if (targetDeviceNo != null) { |
| | |
| | | News.info("[WCS Debug] 任务{}到达目标{}并生成条码", taskNo, finalTargetStationId); |
| | | } |
| | | } |
| | | commandQueue.clear(); |
| | | pendingPathQueue.clear(); |
| | | News.info("[WCS Debug] 任务{}到达目标后清空队列并结束,等待后续新命令重启", taskNo); |
| | | break; |
| | | } |
| | | |
| | | // 未到达最终目标,等待新的分段命令 |
| | | // 继续等待新的分段命令 |
| | | Long lastTime = taskLastUpdateTime.get(taskNo); |
| | | if (lastTime != null && System.currentTimeMillis() - lastTime > 30000) { |
| | | // 超时:30秒内没有收到新分段命令 |
| | |
| | | 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 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); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | if (command.getCommandType() == StationCommandType.WRITE_INFO) { |
| | | if (command.getBarcode() != null) { |
| | | updateStationBarcode(deviceNo, stationId, command.getBarcode()); |
| | | return; |
| | | } |
| | | if (taskNo == 9998 && targetStationId == 0) { |
| | | // 生成出库站点仿真数据 |
| | | generateFakeOutStationData(deviceNo, stationId); |
| | |
| | | } |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | } |
| | | |
| | | // segmentedPathCommand 方法已删除,功能已整合到 runTaskLoop |
| | | |
| | | private Integer getDeviceNoByStationId(Integer stationId) { |
| | |
| | | Object lock = redisUtil.get(RedisKeyType.CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo()); |
| | | if (lock == null) { |
| | | OperateResult taskClearResult = siemensNet.Write("DB100.0", array); |
| | | redisUtil.set(RedisKeyType.CLEAR_CRN_TASK_LIMIT.key + deviceConfig.getDeviceNo(), "lock", 3); |
| | | redisUtil.set(RedisKeyType.CLEAR_CRN_TASK_LIMIT.key + deviceConfig.getDeviceNo(), "lock", 1); |
| | | } |
| | | } |
| | | } |
| | |
| | | @Override |
| | | public CommandResponse sendCommand(CrnCommand command) { |
| | | RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class); |
| | | redisUtil.set(RedisKeyType.CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo(), "lock", 10); |
| | | redisUtil.set(RedisKeyType.CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo(), "lock", 2); |
| | | CommandResponse response = new CommandResponse(false); |
| | | try { |
| | | if (null == command) { |
| | |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】[id:{1}] >>>>> 命令下发: {2}", DateUtils.convert(new Date()), command.getCrnNo(), JSON.toJSON(command))); |
| | | response.setResult(true); |
| | | response.setMessage("命令下发成功"); |
| | | redisUtil.set(RedisKeyType.CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo(), "lock", 5); |
| | | redisUtil.set(RedisKeyType.CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo(), "lock", 1); |
| | | } else { |
| | | News.error("写入堆垛机plc数据失败 ===>> [id:{}]", command.getCrnNo()); |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】写入堆垛机plc数据失败 ===>> [id:{1}]", DateUtils.convert(new Date()), command.getCrnNo())); |
| | |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.News; |
| | | import com.zy.core.cache.OutputQueue; |
| | | import com.zy.core.enums.CrnTaskModeType; |
| | | import com.zy.core.enums.DualCrnTaskModeType; |
| | | import com.zy.core.enums.RedisKeyType; |
| | | import com.zy.core.model.CommandResponse; |
| | | import com.zy.core.model.command.DualCrnCommand; |
| | |
| | | |
| | | import java.text.MessageFormat; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | |
| | | public class ZyDualCrnRealConnect implements ZyDualCrnConnectApi { |
| | | |
| | |
| | | crnStatus.setXDuration((double) siemensNet.getByteTransform().TransSingle(result.Content, 62)); |
| | | crnStatus.setYDuration((double) siemensNet.getByteTransform().TransSingle(result.Content, 66)); |
| | | |
| | | if ((crnStatus.getStatus() == 0 || crnStatus.getStatus() == 20) && crnStatus.getTaskReceive() == 0) { |
| | | OperateResultExOne<byte[]> taskResult = siemensNet.Read("DB100.0", (short) 18); |
| | | if (taskResult.IsSuccess) { |
| | | short taskNo = siemensNet.getByteTransform().TransInt16(taskResult.Content, 0); |
| | |
| | | short destinationPosZ = siemensNet.getByteTransform().TransInt16(taskResult.Content, 14); |
| | | short confirm = siemensNet.getByteTransform().TransInt16(taskResult.Content, 16); |
| | | |
| | | int hasData = 0; |
| | | if(taskNo != 0 || taskMode != 0 || sourcePosX != 0 || sourcePosY != 0 || sourcePosZ != 0 || destinationPosX != 0 || destinationPosY != 0 || destinationPosZ != 0 || confirm != 0) { |
| | | hasData = 1; |
| | | if (crnStatus.getTaskReceive() == 1) { |
| | | HashMap<String, Object> map = new HashMap<>(); |
| | | map.put("taskNo", taskNo); |
| | | map.put("taskMode", taskMode); |
| | | map.put("sourcePosX", sourcePosX); |
| | | map.put("sourcePosY", sourcePosY); |
| | | map.put("sourcePosZ", sourcePosZ); |
| | | map.put("destinationPosX", destinationPosX); |
| | | map.put("destinationPosY", destinationPosY); |
| | | map.put("destinationPosZ", destinationPosZ); |
| | | map.put("confirm", confirm); |
| | | |
| | | short[] array = new short[9]; |
| | | array[0] = (short) 0; |
| | | array[1] = (short) 0; |
| | |
| | | array[7] = (short) 0; |
| | | array[8] = (short) 0; |
| | | |
| | | boolean clear = false; |
| | | if (taskMode == DualCrnTaskModeType.CONFIRM.id) { |
| | | // if (crnStatus.getStatus() == 0) { |
| | | // clear = true; |
| | | // } |
| | | clear = true; |
| | | } else { |
| | | clear = true; |
| | | } |
| | | |
| | | if (clear) { |
| | | RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class); |
| | | Object lock = redisUtil.get(RedisKeyType.DUAL_CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo()); |
| | | if (lock == null) { |
| | | OperateResult taskClearResult = siemensNet.Write("DB100.0", array); |
| | | redisUtil.set(RedisKeyType.CLEAR_DUAL_CRN_TASK_LIMIT.key + deviceConfig.getDeviceNo(), "lock", 3); |
| | | News.info("双工位堆垛机:{}号{}工位命令区域清空,原始数据:{}", deviceConfig.getDeviceNo(), 1, JSON.toJSONString(map)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | crnStatus.setTaskSend(hasData); |
| | | } |
| | | |
| | | if ((crnStatus.getStatusTwo() == 0 || crnStatus.getStatusTwo() == 20) && crnStatus.getTaskReceiveTwo() == 0) { |
| | | OperateResultExOne<byte[]> taskResult = siemensNet.Read("DB100.18", (short) 18); |
| | | if (taskResult.IsSuccess) { |
| | | short taskNo = siemensNet.getByteTransform().TransInt16(taskResult.Content, 0); |
| | | short taskMode = siemensNet.getByteTransform().TransInt16(taskResult.Content, 2); |
| | | short sourcePosX = siemensNet.getByteTransform().TransInt16(taskResult.Content, 4); |
| | | short sourcePosY = siemensNet.getByteTransform().TransInt16(taskResult.Content, 6); |
| | | short sourcePosZ = siemensNet.getByteTransform().TransInt16(taskResult.Content, 8); |
| | | short destinationPosX = siemensNet.getByteTransform().TransInt16(taskResult.Content, 10); |
| | | short destinationPosY = siemensNet.getByteTransform().TransInt16(taskResult.Content, 12); |
| | | short destinationPosZ = siemensNet.getByteTransform().TransInt16(taskResult.Content, 14); |
| | | short confirm = siemensNet.getByteTransform().TransInt16(taskResult.Content, 16); |
| | | OperateResultExOne<byte[]> taskResult2 = siemensNet.Read("DB100.18", (short) 18); |
| | | if (taskResult2.IsSuccess) { |
| | | short taskNo = siemensNet.getByteTransform().TransInt16(taskResult2.Content, 0); |
| | | short taskMode = siemensNet.getByteTransform().TransInt16(taskResult2.Content, 2); |
| | | short sourcePosX = siemensNet.getByteTransform().TransInt16(taskResult2.Content, 4); |
| | | short sourcePosY = siemensNet.getByteTransform().TransInt16(taskResult2.Content, 6); |
| | | short sourcePosZ = siemensNet.getByteTransform().TransInt16(taskResult2.Content, 8); |
| | | short destinationPosX = siemensNet.getByteTransform().TransInt16(taskResult2.Content, 10); |
| | | short destinationPosY = siemensNet.getByteTransform().TransInt16(taskResult2.Content, 12); |
| | | short destinationPosZ = siemensNet.getByteTransform().TransInt16(taskResult2.Content, 14); |
| | | short confirm = siemensNet.getByteTransform().TransInt16(taskResult2.Content, 16); |
| | | |
| | | int hasData = 0; |
| | | if(taskNo != 0 || taskMode != 0 || sourcePosX != 0 || sourcePosY != 0 || sourcePosZ != 0 || destinationPosX != 0 || destinationPosY != 0 || destinationPosZ != 0 || confirm != 0) { |
| | | hasData = 1; |
| | | if (crnStatus.getTaskReceiveTwo() == 1) { |
| | | HashMap<String, Object> map = new HashMap<>(); |
| | | map.put("taskNo", taskNo); |
| | | map.put("taskMode", taskMode); |
| | | map.put("sourcePosX", sourcePosX); |
| | | map.put("sourcePosY", sourcePosY); |
| | | map.put("sourcePosZ", sourcePosZ); |
| | | map.put("destinationPosX", destinationPosX); |
| | | map.put("destinationPosY", destinationPosY); |
| | | map.put("destinationPosZ", destinationPosZ); |
| | | map.put("confirm", confirm); |
| | | |
| | | short[] array = new short[9]; |
| | | array[0] = (short) 0; |
| | | array[1] = (short) 0; |
| | |
| | | array[7] = (short) 0; |
| | | array[8] = (short) 0; |
| | | |
| | | boolean clear = false; |
| | | if (taskMode == DualCrnTaskModeType.CONFIRM.id) { |
| | | // if (crnStatus.getStatusTwo() == 0) { |
| | | // clear = true; |
| | | // } |
| | | clear = true; |
| | | } else { |
| | | clear = true; |
| | | } |
| | | |
| | | if (clear) { |
| | | RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class); |
| | | Object lock = redisUtil.get(RedisKeyType.DUAL_CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo()); |
| | | if (lock == null) { |
| | | OperateResult taskClearResult = siemensNet.Write("DB100.18", array); |
| | | redisUtil.set(RedisKeyType.CLEAR_DUAL_CRN_TASK_LIMIT.key + deviceConfig.getDeviceNo(), "lock", 3); |
| | | News.info("双工位堆垛机:{}号{}工位命令区域清空,原始数据:{}", deviceConfig.getDeviceNo(), 2, JSON.toJSONString(map)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | crnStatus.setTaskSendTwo(hasData); |
| | | } |
| | | |
| | | return crnStatus; |
| | |
| | | @Override |
| | | public CommandResponse sendCommand(DualCrnCommand command) { |
| | | RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo(), "lock", 10); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo(), "lock", 3); |
| | | CommandResponse response = new CommandResponse(false); |
| | | try { |
| | | if (null == command) { |
| | |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】[id:{1}] >>>>> 命令下发: {2}", DateUtils.convert(new Date()), command.getCrnNo(), JSON.toJSON(command))); |
| | | response.setResult(true); |
| | | response.setMessage("命令下发成功"); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo(), "lock", 5); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_SEND_COMMAND_LOCK.key + deviceConfig.getDeviceNo(), "lock", 3); |
| | | } else { |
| | | News.error("双工位堆垛机写入堆垛机plc数据失败 ===>> [id:{}]", command.getCrnNo()); |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】写入堆垛机plc数据失败 ===>> [id:{1}]", DateUtils.convert(new Date()), command.getCrnNo())); |
| | |
| | | if (barcodeEntity == null) { |
| | | continue; |
| | | } |
| | | String barcode = siemensNet.getByteTransform().TransString(result2.Content, i * 16 + 2, 14, "UTF-8"); |
| | | byte length = 10;//result2.Content[1]; |
| | | String barcode = siemensNet.getByteTransform().TransString(result2.Content, i * 16 + 2, length, "UTF-8"); |
| | | barcode = barcode.trim(); |
| | | barcodeEntity.setBarcode(barcode); |
| | | } |
| | |
| | | stationOperateProcessUtils.crnStationOutExecute(); |
| | | // 检测输送站点出库任务执行完成 |
| | | stationOperateProcessUtils.stationOutExecuteFinish(); |
| | | // 检测任务转完成 |
| | | stationOperateProcessUtils.checkTaskToComplete(); |
| | | // 检测出库排序 |
| | | stationOperateProcessUtils.checkStationOutOrder(); |
| | | // 监控绕圈站点 |
| | | stationOperateProcessUtils.watchCircleStation(); |
| | | |
| | | // 执行双工位堆垛机任务 |
| | | dualCrnOperateProcessUtils.dualCrnIoExecute(); |
| | |
| | | |
| | | // 生成仿真模拟出库任务 |
| | | private synchronized void generateFakeOutTask() { |
| | | try { |
| | | if (!enableFake.equals("Y")) { |
| | | return; |
| | | } |
| | |
| | | redisUtil.set(RedisKeyType.GENERATE_FAKE_OUT_TASK_LIMIT.key + stationId, "lock", 10); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | |
| | | if (lock != null) { |
| | | continue; |
| | | } |
| | | redisUtil.set(RedisKeyType.GENERATE_IN_TASK_LIMIT.key + stationId, "lock", 10); |
| | | redisUtil.set(RedisKeyType.GENERATE_IN_TASK_LIMIT.key + stationId, "lock", 3); |
| | | |
| | | String barcode = stationProtocol.getBarcode(); |
| | | Integer stationIdVal = stationProtocol.getStationId(); |
| | |
| | | stationProtocol.getPalletHeight()); |
| | | redisUtil.set(RedisKeyType.GENERATE_IN_TASK_LIMIT.key + stationId, "lock", 2); |
| | | |
| | | stationProtocol.setSystemWarning("请求入库失败,WMS无返回"); |
| | | // stationProtocol.setSystemWarning("请求入库失败,WMS无返回"); |
| | | } |
| | | } |
| | | } |
| | |
| | | continue; |
| | | } |
| | | |
| | | if (System.currentTimeMillis() - stayTime > 1000 * 15) { |
| | | if (System.currentTimeMillis() - stayTime > 1000 * 60) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, |
| | | stationObjModel.getDeviceNo()); |
| | | if (stationThread == null) { |
| | |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getCrnNo()); |
| | | CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo()); |
| | | MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand)); |
| | | News.info("堆垛机任务状态更新成功,堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo()); |
| | | } |
| | |
| | | wmsOperateUtils.applyInTaskAsync(barcode, stationIdVal, |
| | | stationProtocol.getPalletHeight()); |
| | | redisUtil.set(RedisKeyType.GENERATE_IN_TASK_LIMIT.key + stationId, "lock", 2); |
| | | stationProtocol.setSystemWarning("请求入库失败,WMS无返回"); |
| | | // stationProtocol.setSystemWarning("请求入库失败,WMS无返回"); |
| | | } |
| | | } |
| | | } |
| | |
| | | stationOperateProcessUtils.dualCrnStationOutExecute(); |
| | | //检测输送站点出库任务执行完成 |
| | | stationOperateProcessUtils.stationOutExecuteFinish(); |
| | | // 检测任务转完成 |
| | | stationOperateProcessUtils.checkTaskToComplete(); |
| | | //检测输送站点是否运行堵塞 |
| | | stationOperateProcessUtils.checkStationRunBlock(); |
| | | |
| | |
| | | } |
| | | |
| | | if (stationProtocol.getError() > 0) { |
| | | Object lock = redisUtil.get(RedisKeyType.GENERATE_STATION_BACK_LIMIT.key + stationProtocol.getTaskNo()); |
| | | if (lock != null) { |
| | | continue; |
| | | } |
| | | StationObjModel backStation = entity.getBackStation(); |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, commonService.getWorkNo(WrkIoType.STATION_BACK.id), stationId, backStation.getStationId(), 0); |
| | | if (command == null) { |
| | |
| | | } |
| | | MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command)); |
| | | News.taskInfo(stationProtocol.getTaskNo(), "{}扫码异常,已退回至{}", backStation.getStationId()); |
| | | redisUtil.set(RedisKeyType.GENERATE_STATION_BACK_LIMIT.key + stationProtocol.getTaskNo(), "lock", 10); |
| | | continue; |
| | | } |
| | | |
| | | // 检测任务是否生成 |
| | | List<WrkMast> wrkMasts = wrkMastService |
| | | .selectList(new EntityWrapper<WrkMast>().eq("barcode", stationProtocol.getBarcode())); |
| | | .selectList(new EntityWrapper<WrkMast>() |
| | | .eq("barcode", stationProtocol.getBarcode())); |
| | | if (!wrkMasts.isEmpty()) { |
| | | continue; |
| | | } |
| | |
| | | wmsOperateUtils.applyInTaskAsync(barcode, stationIdVal, |
| | | stationProtocol.getPalletHeight()); |
| | | redisUtil.set(RedisKeyType.GENERATE_IN_TASK_LIMIT.key + stationId, "lock", 2); |
| | | stationProtocol.setSystemWarning("请求入库失败,WMS无返回"); |
| | | // stationProtocol.setSystemWarning("请求入库失败,WMS无返回"); |
| | | } |
| | | } |
| | | } |
| | |
| | | mapNode.put("id", i + "-" + j); |
| | | |
| | | String nodeType = map.getString("type"); |
| | | mapNode.put("type", nodeType); |
| | | if("shelf".equals(nodeType)) { |
| | | mapNode.put("value", MapNodeType.NORMAL_PATH.id); |
| | | } else if ("devp".equals(nodeType)) { |
| | | mapNode.put("value", MapNodeType.DISABLE.id); |
| | | } else if ("crn".equals(nodeType) || "dualCrn".equals(nodeType) || "rgv".equals(nodeType)) { |
| | | mapNode.put("value", MapNodeType.MAIN_PATH.id); |
| | | }else { |
| | | mapNode.put("value", MapNodeType.DISABLE.id); |
| | | } |
| | |
| | | |
| | | CrnCommand getMoveCommand(String targetLocNo, Integer taskNo, Integer crnNo);//移动 |
| | | |
| | | CrnCommand getResetCommand(Integer crnNo);//复位 |
| | | CrnCommand getResetCommand(Integer taskNo, Integer crnNo);//复位 |
| | | |
| | | CommandResponse sendCommand(CrnCommand command);//下发命令 |
| | | |
| | |
| | | |
| | | DualCrnCommand getMoveCommand(String targetLocNo, Integer taskNo, Integer crnNo);//移动 |
| | | |
| | | DualCrnCommand getResetCommand(Integer crnNo, Integer station);//复位 |
| | | DualCrnCommand getResetCommand(Integer taskNo, Integer crnNo, Integer station);//复位 |
| | | |
| | | CommandResponse sendCommand(DualCrnCommand command);//下发命令 |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public CrnCommand getResetCommand(Integer crnNo) { |
| | | public CrnCommand getResetCommand(Integer taskNo, Integer crnNo) { |
| | | CrnCommand crnCommand = new CrnCommand(); |
| | | crnCommand.setCrnNo(crnNo); // 堆垛机编号 |
| | | crnCommand.setTaskNo(0); // 工作号 |
| | | crnCommand.setTaskNo(taskNo); // 工作号 |
| | | crnCommand.setAckFinish(1); // 任务完成确认位 |
| | | crnCommand.setTaskMode(CrnTaskModeType.NONE.id); // 任务模式 |
| | | crnCommand.setSourcePosX(0); // 源库位排 |
| | |
| | | } |
| | | |
| | | @Override |
| | | public CrnCommand getResetCommand(Integer crnNo) { |
| | | public CrnCommand getResetCommand(Integer taskNo, Integer crnNo) { |
| | | CrnCommand crnCommand = new CrnCommand(); |
| | | crnCommand.setCrnNo(crnNo); // 堆垛机编号 |
| | | crnCommand.setTaskNo(0); // 工作号 |
| | | crnCommand.setTaskNo(taskNo); // 工作号 |
| | | crnCommand.setAckFinish(1); // 任务完成确认位 |
| | | crnCommand.setTaskMode(CrnTaskModeType.RESET.id); // 任务模式 |
| | | crnCommand.setSourcePosX(0); // 源库位排 |
| | |
| | | import com.zy.asrs.entity.*; |
| | | import com.zy.asrs.service.BasDualCrnpOptService; |
| | | import com.zy.asrs.service.BasDualCrnpService; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.asrs.utils.Utils; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.News; |
| | |
| | | private RedisUtil redisUtil; |
| | | private ZyDualCrnConnectDriver zyDualCrnConnectDriver; |
| | | private DualCrnProtocol crnProtocol; |
| | | private int deviceLogCollectTime = 200; |
| | | private volatile int deviceLogCollectTime = 200; |
| | | |
| | | public ZySiemensDualCrnThread(DeviceConfig deviceConfig, RedisUtil redisUtil) { |
| | | this.deviceConfig = deviceConfig; |
| | |
| | | try { |
| | | deviceLogCollectTime = Utils.getDeviceLogCollectTime(); |
| | | readStatus(); |
| | | Thread.sleep(50); |
| | | Thread.sleep(100); |
| | | } catch (Exception e) { |
| | | log.error("DualCrnThread Fail", e); |
| | | } |
| | |
| | | List<SendDualCrnCommandParam> commandList = (List<SendDualCrnCommandParam>) task.getData(); |
| | | for (SendDualCrnCommandParam sendDualCrnCommandParam : commandList) { |
| | | DualCrnCommand dualCrnCommand = sendDualCrnCommandParam.getCommands().get(0); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_COMMAND_IDX.key + dualCrnCommand.getTaskNo(), sendDualCrnCommandParam.getCommandIdx(), 60 * 60 * 24); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_COMMAND_IDX.key + dualCrnCommand.getTaskNo(), 0, 60 * 60 * 24); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_COMMAND_.key + dualCrnCommand.getTaskNo(), JSON.toJSONString(sendDualCrnCommandParam, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24); |
| | | if (sendDualCrnCommandParam.getStation() == 1) { |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_STATION1_FLAG.key + crnProtocol.getCrnNo(), dualCrnCommand.getTaskNo(), 60 * 60 * 24); |
| | | } else { |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_STATION2_FLAG.key + crnProtocol.getCrnNo(), dualCrnCommand.getTaskNo(), 60 * 60 * 24); |
| | | } |
| | | |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_COMMAND_.key + crnProtocol.getCrnNo(), JSON.toJSONString(commandList, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24); |
| | | } |
| | | } else if (step == 3) { |
| | | sendCommand((DualCrnCommand) task.getData()); |
| | | } |
| | |
| | | Thread commandThread = new Thread(() -> { |
| | | while (true) { |
| | | try { |
| | | Thread.sleep(100); |
| | | |
| | | if(crnProtocol.getMode() != DualCrnModeType.AUTO.id) { |
| | | continue; |
| | | } |
| | |
| | | continue; |
| | | } |
| | | |
| | | Object commandListObj = redisUtil.get(RedisKeyType.DUAL_CRN_COMMAND_.key + crnProtocol.getCrnNo()); |
| | | if (commandListObj == null) { |
| | | continue; |
| | | } |
| | | List<SendDualCrnCommandParam> commandList = JSON.parseArray(commandListObj.toString(), SendDualCrnCommandParam.class); |
| | | List<SendDualCrnCommandParam> commandList = getDualCrnCommandList(); |
| | | for (SendDualCrnCommandParam commandParam : commandList) { |
| | | processStation(commandParam); |
| | | } |
| | | |
| | | Thread.sleep(100); |
| | | } catch (Exception e) { |
| | | log.error("DualCrnCommand Fail", e); |
| | | } |
| | |
| | | DualCrnCommand dualCommand = commandList.get(idx); |
| | | |
| | | boolean send = false; |
| | | //TODO 判断站点1,2任务类型是否一致 |
| | | if (station == 1) { |
| | | if (crnProtocol.getStatus().equals(DualCrnStatusType.FETCH_COMPLETE.id) |
| | | || crnProtocol.getStatus().equals(DualCrnStatusType.IDLE.id) |
| | | ) { |
| | | log.info("工位2任务==========>{}, 任务模式---->{}", crnProtocol.getTaskNoTwo(), crnProtocol.getMode()); |
| | | log.info("工位1任务==========>{}, 任务模式---->{}", crnProtocol.getTaskNo(), crnProtocol.getMode()); |
| | | Integer taskNo = crnProtocol.getTaskNoTwo(); |
| | | if (crnProtocol.getTaskSend() == 0) { |
| | | //两个工位只允许放相同类型任务 |
| | | // if (taskNo != null && taskNo > 0) { |
| | | // WrkMastService mastService = SpringUtils.getBean(WrkMastService.class); |
| | | // WrkMast wrkMast = mastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_no", taskNo)); |
| | | // WrkMast wrkMast1 = mastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_no", dualCommand.getTaskNo())); |
| | | // if (!Objects.isNull(wrkMast) && !Objects.isNull(wrkMast1)) { |
| | | // if (wrkMast1.getIoType() != wrkMast.getIoType()) { |
| | | //// return; |
| | | // } |
| | | // } |
| | | // } |
| | | // && sendComm(taskNo, dualCommand.getTaskNo()) |
| | | if (dualCommand.getTaskMode().intValue() == DualCrnTaskModeType.PICK.id) { |
| | | if (crnProtocol.getStatus().equals(DualCrnStatusType.IDLE.id)) { |
| | | send = true; |
| | | } |
| | | }else { |
| | | if (crnProtocol.getStatusTwo().equals(DualCrnStatusType.FETCH_COMPLETE.id) |
| | | || crnProtocol.getStatusTwo().equals(DualCrnStatusType.IDLE.id) |
| | | ) { |
| | | } else if (dualCommand.getTaskMode().intValue() == DualCrnTaskModeType.PUT.id) { |
| | | if (crnProtocol.getStatus().equals(DualCrnStatusType.FETCH_COMPLETE.id)) { |
| | | send = true; |
| | | } |
| | | } |
| | | } |
| | | } else { |
| | | log.info("工位1任务==========>{}, 任务模式---->{}", crnProtocol.getTaskNo(), crnProtocol.getMode()); |
| | | log.info("工位2任务==========>{}, 任务模式---->{}", crnProtocol.getTaskNoTwo(), crnProtocol.getMode()); |
| | | Integer taskNo = crnProtocol.getTaskNo(); |
| | | // && sendComm(taskNo, dualCommand.getTaskNo()) |
| | | if (crnProtocol.getTaskSendTwo() == 0) { |
| | | if (dualCommand.getTaskMode().intValue() == DualCrnTaskModeType.PICK.id) { |
| | | if (crnProtocol.getStatusTwo().equals(DualCrnStatusType.IDLE.id)) { |
| | | send = true; |
| | | } |
| | | } else if (dualCommand.getTaskMode().intValue() == DualCrnTaskModeType.PUT.id) { |
| | | if (crnProtocol.getStatusTwo().equals(DualCrnStatusType.FETCH_COMPLETE.id)) { |
| | | send = true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | idx++; |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_COMMAND_IDX.key + firstCommand.getTaskNo(), idx, 60 * 60 * 24); |
| | | sendCommand(dualCommand); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_PICK_WAIT_NEXT_TASK.key + crnProtocol.getCrnNo(), "lock", 5); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_PICK_WAIT_NEXT_TASK.key + crnProtocol.getCrnNo(), "lock", 3); |
| | | } |
| | | return; |
| | | }else { |
| | |
| | | } |
| | | } |
| | | |
| | | public boolean sendComm(Integer taskNo, Integer current) { |
| | | //两个工位只允许放相同类型任务 |
| | | if (taskNo != null && taskNo > 0) { |
| | | WrkMastService mastService = SpringUtils.getBean(WrkMastService.class); |
| | | WrkMast wrkMast = mastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_no", taskNo)); |
| | | WrkMast wrkMast1 = mastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_no", current)); |
| | | if (!Objects.isNull(wrkMast) && !Objects.isNull(wrkMast1)) { |
| | | if (wrkMast1.getIoType() != wrkMast.getIoType()) { |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 初始化堆垛机状态 |
| | | */ |
| | |
| | | |
| | | //工位1 |
| | | crnProtocol.setTaskNo(0); |
| | | crnProtocol.setDeviceTaskNo(0); |
| | | crnProtocol.setStatus(-1); |
| | | crnProtocol.setBay(0); |
| | | crnProtocol.setLevel(0); |
| | |
| | | |
| | | //工位2 |
| | | crnProtocol.setTaskNoTwo(0); |
| | | crnProtocol.setDeviceTaskNoTwo(0); |
| | | crnProtocol.setStatusTwo(-1); |
| | | crnProtocol.setBayTwo(0); |
| | | crnProtocol.setLevelTwo(0); |
| | |
| | | crnProtocol.setMode(crnStatus.getMode()); |
| | | |
| | | //工位1 |
| | | crnProtocol.setTaskNo(crnStatus.getTaskNo()); |
| | | crnProtocol.setDeviceTaskNo(crnStatus.getTaskNo()); |
| | | crnProtocol.setStatus(crnStatus.getStatus()); |
| | | crnProtocol.setForkPos(crnStatus.getForkPos()); |
| | | crnProtocol.setLoaded(crnStatus.getLoaded()); |
| | | crnProtocol.setTaskReceive(crnStatus.getTaskReceive()); |
| | | crnProtocol.setTaskSend(crnStatus.getTaskSend()); |
| | | |
| | | //工位2 |
| | | crnProtocol.setTaskNoTwo(crnStatus.getTaskNoTwo()); |
| | | crnProtocol.setDeviceTaskNoTwo(crnStatus.getTaskNoTwo()); |
| | | crnProtocol.setStatusTwo(crnStatus.getStatusTwo()); |
| | | crnProtocol.setForkPosTwo(crnStatus.getForkPosTwo()); |
| | | crnProtocol.setLoadedTwo(crnStatus.getLoadedTwo()); |
| | | crnProtocol.setTaskReceiveTwo(crnStatus.getTaskReceiveTwo()); |
| | | crnProtocol.setTaskSendTwo(crnStatus.getTaskSendTwo()); |
| | | |
| | | crnProtocol.setBay(crnStatus.getBay()); |
| | | crnProtocol.setLevel(crnStatus.getLevel()); |
| | |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception ignore) {} |
| | | } catch (Exception ignore) { |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | @Override |
| | | public DualCrnCommand getResetCommand(Integer crnNo, Integer station) { |
| | | public DualCrnCommand getResetCommand(Integer taskNo, Integer crnNo, Integer station) { |
| | | DualCrnCommand crnCommand = new DualCrnCommand(); |
| | | crnCommand.setCrnNo(crnNo); // 堆垛机编号 |
| | | crnCommand.setTaskNo(0); // 工作号 |
| | | crnCommand.setTaskNo(taskNo); // 工作号 |
| | | crnCommand.setTaskMode(DualCrnTaskModeType.CONFIRM.id); // 任务模式: 确认 |
| | | crnCommand.setSourcePosX(0); // 源库位排 |
| | | crnCommand.setSourcePosY(0); // 源库位列 |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | public List<SendDualCrnCommandParam> getDualCrnCommandList() { |
| | | List<SendDualCrnCommandParam> commandList = new ArrayList<>(); |
| | | SendDualCrnCommandParam command1 = getDualCrnCommandList(1); |
| | | SendDualCrnCommandParam command2 = getDualCrnCommandList(2); |
| | | if (command1 != null) { |
| | | commandList.add(command1); |
| | | } |
| | | |
| | | if (command2 != null) { |
| | | commandList.add(command2); |
| | | } |
| | | |
| | | return commandList; |
| | | } |
| | | |
| | | public SendDualCrnCommandParam getDualCrnCommandList(int station) { |
| | | SendDualCrnCommandParam sendDualCrnCommandParam = null; |
| | | String key = null; |
| | | if (station == 1 && crnProtocol.getTaskNo() > 0) { |
| | | key = RedisKeyType.DUAL_CRN_COMMAND_.key + crnProtocol.getTaskNo(); |
| | | } else if (station == 2 && crnProtocol.getTaskNoTwo() > 0) { |
| | | key = RedisKeyType.DUAL_CRN_COMMAND_.key + crnProtocol.getTaskNoTwo(); |
| | | } |
| | | |
| | | if (key == null) { |
| | | return null; |
| | | } |
| | | Object commandObj = redisUtil.get(key); |
| | | |
| | | if (commandObj == null) { |
| | | return null; |
| | | } |
| | | |
| | | sendDualCrnCommandParam = JSON.parseObject(commandObj.toString(), SendDualCrnCommandParam.class); |
| | | if (sendDualCrnCommandParam == null) { |
| | | return null; |
| | | } |
| | | |
| | | if (station == 1) { |
| | | if (crnProtocol.getTaskNoTwo() > 0) { |
| | | Object object = redisUtil.get(RedisKeyType.DUAL_CRN_COMMAND_.key + crnProtocol.getTaskNoTwo()); |
| | | if (object != null) { |
| | | SendDualCrnCommandParam jsonObject = JSON.parseObject(object.toString(), SendDualCrnCommandParam.class); |
| | | ; |
| | | if (!jsonObject.getCommands().get(0).getTaskMode().equals(sendDualCrnCommandParam.getCommands().get(0).getTaskMode())) { |
| | | return null; |
| | | } |
| | | } |
| | | } |
| | | } else { |
| | | if (crnProtocol.getTaskNo() > 0) { |
| | | WrkMastService mastService = SpringUtils.getBean(WrkMastService.class); |
| | | WrkMast wrkMast = mastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_no", crnProtocol.getTaskNo())); |
| | | WrkMast wrkMast1 = mastService.selectOne(new EntityWrapper<WrkMast>().eq("wrk_no", crnProtocol.getTaskNoTwo())); |
| | | if (!Objects.isNull(wrkMast) && !Objects.isNull(wrkMast1)) { |
| | | if (!wrkMast1.getIoType().equals(wrkMast.getIoType())) { |
| | | return null; |
| | | } |
| | | } |
| | | // Object object = redisUtil.get(RedisKeyType.DUAL_CRN_COMMAND_.key + crnProtocol.getTaskNo()); |
| | | // if (object != null) { |
| | | // SendDualCrnCommandParam jsonObject = JSON.parseObject(object.toString(), SendDualCrnCommandParam.class); |
| | | // ; |
| | | // if (!jsonObject.getCommands().get(0).getTaskMode().equals(sendDualCrnCommandParam.getCommands().get(0).getTaskMode())) { |
| | | // return null; |
| | | // } |
| | | // } |
| | | } |
| | | } |
| | | |
| | | return sendDualCrnCommandParam; |
| | | } |
| | | } |
| | |
| | | private CommonService commonService; |
| | | @Autowired |
| | | private NotifyUtils notifyUtils; |
| | | @Autowired |
| | | private StationOperateProcessUtils stationOperateProcessUtils; |
| | | |
| | | public synchronized void crnIoExecute() { |
| | | Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | |
| | | List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>() |
| | | .eq("crn_no", basCrnp.getCrnNo()) |
| | | .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts) |
| | | .orderBy("batch_seq", false) |
| | | ); |
| | | if(!wrkMasts.isEmpty()){ |
| | | continue; |
| | |
| | | return false; |
| | | } |
| | | |
| | | int stationMaxTaskCount = 30; |
| | | Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (systemConfigMapObj != null) { |
| | | try { |
| | | HashMap<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj; |
| | | stationMaxTaskCount = Integer.parseInt(systemConfigMap.getOrDefault("stationMaxTaskCountLimit", "30")); |
| | | } catch (Exception ignore) {} |
| | | } |
| | | |
| | | int currentStationTaskCount = stationOperateProcessUtils.getCurrentStationTaskCount(); |
| | | if (stationMaxTaskCount > 0 && currentStationTaskCount >= stationMaxTaskCount) { |
| | | News.warn("输送站点任务数量达到上限,已停止任务下发。当前任务数={},上限={}", currentStationTaskCount, stationMaxTaskCount); |
| | | return false; |
| | | } |
| | | |
| | | Integer crnNo = basCrnp.getCrnNo(); |
| | | |
| | | List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>() |
| | | .eq("crn_no", crnNo) |
| | | .eq("wrk_sts", WrkStsType.NEW_OUTBOUND.sts) |
| | | .orderBy("batch_seq", false) |
| | | ); |
| | | |
| | | for (WrkMast wrkMast : wrkMasts) { |
| | |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getCrnNo()); |
| | | CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo()); |
| | | MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand)); |
| | | News.info("堆垛机任务状态更新成功,堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo()); |
| | | } |
| | |
| | | String shallowLocNo = Utils.getLocNo(shallowRow, Utils.getBay(locNo), Utils.getLev(locNo)); |
| | | LocMast shallowLocMast = locMastService.queryByLoc(shallowLocNo); |
| | | if (shallowLocMast == null) { |
| | | News.taskInfo(taskNo, "浅库位:{} 数据不存在", shallowLocNo); |
| | | return false; |
| | | return true; |
| | | } |
| | | |
| | | if (shallowLocMast.getLocSts().equals("O")) { |
| | |
| | | continue; |
| | | } |
| | | |
| | | if(dualCrnProtocol.getTaskSend() != 0 || dualCrnProtocol.getTaskSendTwo() != 0) { |
| | | continue; |
| | | } |
| | | |
| | | this.crnExecute(basDualCrnp, dualCrnThread); |
| | | } |
| | | } |
| | |
| | | WrkMast stationOneWrkMast = null; |
| | | WrkMast stationTwoWrkMast = null; |
| | | |
| | | List<Integer> disableList = basDualCrnp.getDisableStationOneBays$(); |
| | | List<Integer> disableOneList = basDualCrnp.getDisableStationOneBays$(); |
| | | List<Integer> disableTwoList = basDualCrnp.getDisableStationTwoBays$(); |
| | | |
| | | for (WrkMast wrkMast : outTaskList) { |
| | | if (stationOneWrkMast == null) { |
| | | if (!disableList.contains(Utils.getBay(wrkMast.getSourceLocNo()))) { |
| | | if (!disableOneList.contains(Utils.getBay(wrkMast.getSourceLocNo()))) { |
| | | stationOneWrkMast = wrkMast; |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | if (stationTwoWrkMast == null) { |
| | | if (!disableList.contains(Utils.getBay(wrkMast.getSourceLocNo()))) { |
| | | if (!disableTwoList.contains(Utils.getBay(wrkMast.getSourceLocNo()))) { |
| | | stationTwoWrkMast = wrkMast; |
| | | continue; |
| | | } |
| | |
| | | |
| | | Integer station = inStationObjModel.getDualCrnExecuteStation(); |
| | | if (station == 1) { |
| | | if (dualCrnProtocol.getTaskNo() > 0) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "工位1系统内部记录有任务"); |
| | | return null; |
| | | } |
| | | List<Integer> basList = basDualCrnp.getDisableStationOneBays$(); |
| | | if (basList.contains(Utils.getBay(wrkMast.getLocNo()))) { |
| | | //禁止放货列,申请重新分配 |
| | |
| | | return null; |
| | | } |
| | | }else { |
| | | if (dualCrnProtocol.getTaskNoTwo() > 0) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "工位2系统内部记录有任务"); |
| | | return null; |
| | | } |
| | | List<Integer> basList = basDualCrnp.getDisableStationTwoBays$(); |
| | | if (basList.contains(Utils.getBay(wrkMast.getLocNo()))) { |
| | | //禁止放货列,申请重新分配 |
| | |
| | | if (outStationList.isEmpty()) { |
| | | News.info("双工位堆垛机:{} 出库站点未设置", basDualCrnp.getCrnNo()); |
| | | return null; |
| | | } |
| | | |
| | | if (station == 1) { |
| | | if (dualCrnProtocol.getTaskNo() > 0) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "工位1系统内部记录有任务"); |
| | | return null; |
| | | } |
| | | }else { |
| | | if (dualCrnProtocol.getTaskNoTwo() > 0) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "工位2系统内部记录有任务"); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | Integer crnNo = basDualCrnp.getCrnNo(); |
| | |
| | | continue; |
| | | } |
| | | |
| | | if(dualCrnProtocol.getTaskNo() > 0 && dualCrnProtocol.getStatus() == DualCrnStatusType.WAITING.id) { |
| | | if((dualCrnProtocol.getTaskNo() > 0 && dualCrnProtocol.getDeviceTaskNo() > 0) && dualCrnProtocol.getTaskSend() == 0 && dualCrnProtocol.getStatus().equals(DualCrnStatusType.WAITING.id)) { |
| | | executeFinish(basDualCrnp, dualCrnThread, dualCrnProtocol, dualCrnProtocol.getTaskNo(), 1); |
| | | continue; |
| | | } |
| | | |
| | | if(dualCrnProtocol.getTaskNoTwo() > 0 && dualCrnProtocol.getStatusTwo() == DualCrnStatusType.WAITING.id) { |
| | | if((dualCrnProtocol.getTaskNoTwo() > 0 && dualCrnProtocol.getDeviceTaskNoTwo() > 0) && dualCrnProtocol.getTaskSendTwo() == 0 && dualCrnProtocol.getStatusTwo().equals(DualCrnStatusType.WAITING.id)) { |
| | | executeFinish(basDualCrnp, dualCrnThread, dualCrnProtocol, dualCrnProtocol.getTaskNoTwo(), 2); |
| | | continue; |
| | | } |
| | | } |
| | | } |
| | |
| | | return; |
| | | } |
| | | |
| | | DualCrnCommand resetCommand = dualCrnThread.getResetCommand(dualCrnProtocol.getCrnNo(), station); |
| | | DualCrnCommand resetCommand = dualCrnThread.getResetCommand(taskNo, dualCrnProtocol.getCrnNo(), station); |
| | | boolean offer = MessageQueue.offer(SlaveType.DualCrn, dualCrnProtocol.getCrnNo(), new Task(3, resetCommand)); |
| | | if (offer) { |
| | | if (station == 1) { |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_STATION1_FLAG.key + basDualCrnp.getCrnNo(), 0, 60 * 60 * 24); |
| | | }else { |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_STATION2_FLAG.key + basDualCrnp.getCrnNo(), 0, 60 * 60 * 24); |
| | | } |
| | | |
| | | wrkMast.setWrkSts(updateWrkSts); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(new Date()); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | News.info("双工位堆垛机任务状态更新成功,堆垛机号={},工作号={}", basDualCrnp.getCrnNo(), taskNo); |
| | | } |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_IO_EXECUTE_FINISH_LIMIT.key + basDualCrnp.getCrnNo() + "_" + taskNo, "lock", 10); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_IO_EXECUTE_FINISH_LIMIT.key + basDualCrnp.getCrnNo() + "_" + taskNo, "lock", 3); |
| | | } |
| | | }else { |
| | | DualCrnCommand resetCommand = dualCrnThread.getResetCommand(dualCrnProtocol.getCrnNo(), station); |
| | | DualCrnCommand resetCommand = dualCrnThread.getResetCommand(taskNo, dualCrnProtocol.getCrnNo(), station); |
| | | MessageQueue.offer(SlaveType.DualCrn, dualCrnProtocol.getCrnNo(), new Task(3, resetCommand)); |
| | | News.info("双工位堆垛机命令完成确认成功,堆垛机号={},工作号={}", basDualCrnp.getCrnNo(), taskNo); |
| | | redisUtil.set(RedisKeyType.DUAL_CRN_IO_EXECUTE_FINISH_LIMIT.key + basDualCrnp.getCrnNo() + "_" + taskNo, "lock", 3); |
| | | } |
| | | } |
| | | |
| | |
| | | String shallowLocNo = Utils.getLocNo(shallowRow, Utils.getBay(locNo), Utils.getLev(locNo)); |
| | | LocMast shallowLocMast = locMastService.queryByLoc(shallowLocNo); |
| | | if (shallowLocMast == null) { |
| | | News.taskInfo(taskNo, "浅库位:{} 数据不存在", shallowLocNo); |
| | | return false; |
| | | return true; |
| | | } |
| | | |
| | | if (shallowLocMast.getLocSts().equals("O")) { |
| | |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.alibaba.fastjson.serializer.SerializerFeature; |
| | | import com.baomidou.mybatisplus.mapper.EntityWrapper; |
| | | import com.core.common.Cools; |
| | | import com.core.exception.CoolException; |
| | | import com.zy.asrs.domain.enums.NotifyMsgType; |
| | | import com.zy.asrs.domain.vo.StationCycleCapacityVo; |
| | | import com.zy.asrs.domain.vo.StationCycleLoopVo; |
| | | import com.zy.asrs.entity.*; |
| | | import com.zy.asrs.service.*; |
| | | import com.zy.asrs.utils.NotifyUtils; |
| | | import com.zy.common.entity.FindCrnNoResult; |
| | | import com.zy.common.model.NavigateNode; |
| | | import com.zy.common.model.StartupDto; |
| | | import com.zy.common.service.CommonService; |
| | | import com.zy.common.utils.NavigateUtils; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.News; |
| | | import com.zy.core.cache.MessageQueue; |
| | |
| | | |
| | | @Component |
| | | public class StationOperateProcessUtils { |
| | | private static final int LOOP_LOAD_RESERVE_EXPIRE_SECONDS = 120; |
| | | |
| | | @Autowired |
| | | private BasDevpService basDevpService; |
| | |
| | | @Autowired |
| | | private CommonService commonService; |
| | | @Autowired |
| | | private BasCrnpService basCrnpService; |
| | | @Autowired |
| | | private BasDualCrnpService basDualCrnpService; |
| | | @Autowired |
| | | private RedisUtil redisUtil; |
| | | @Autowired |
| | | private LocMastService locMastService; |
| | |
| | | private WmsOperateUtils wmsOperateUtils; |
| | | @Autowired |
| | | private NotifyUtils notifyUtils; |
| | | @Autowired |
| | | private NavigateUtils navigateUtils; |
| | | @Autowired |
| | | private BasStationService basStationService; |
| | | @Autowired |
| | | private StationCycleCapacityService stationCycleCapacityService; |
| | | |
| | | //执行输送站点入库任务 |
| | | public synchronized void stationInExecute() { |
| | | try { |
| | | DispatchLimitConfig limitConfig = getDispatchLimitConfig(); |
| | | int[] currentStationTaskCountRef = new int[]{countCurrentStationTask()}; |
| | | LoadGuardState loadGuardState = buildLoadGuardState(limitConfig); |
| | | |
| | | List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | |
| | | continue; |
| | | } |
| | | |
| | | LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), targetStationId, loadGuardState); |
| | | |
| | | if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) { |
| | | return; |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationId, targetStationId, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "{}工作,获取输送线命令失败", wrkMast.getWrkNo()); |
| | |
| | | MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command)); |
| | | News.info("输送站点入库命令下发成功,站点号={},工作号={},命令数据={}", stationId, wrkMast.getWrkNo(), JSON.toJSONString(command)); |
| | | redisUtil.set(RedisKeyType.STATION_IN_EXECUTE_LIMIT.key + stationId, "lock", 5); |
| | | loadGuardState.reserveLoopTask(loopHitResult.getLoopNo()); |
| | | saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult); |
| | | } |
| | | } |
| | | } |
| | |
| | | //执行堆垛机输送站点出库任务 |
| | | public synchronized void crnStationOutExecute() { |
| | | try { |
| | | DispatchLimitConfig limitConfig = getDispatchLimitConfig(); |
| | | int[] currentStationTaskCountRef = new int[]{countCurrentStationTask()}; |
| | | LoadGuardState loadGuardState = buildLoadGuardState(limitConfig); |
| | | |
| | | List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>() |
| | | .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts) |
| | | .isNotNull("crn_no") |
| | | ); |
| | | List<Integer> outOrderList = getAllOutOrderList(); |
| | | |
| | | for (WrkMast wrkMast : wrkMasts) { |
| | | Object infoObj = redisUtil.get(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + wrkMast.getWrkNo()); |
| | | if (infoObj == null) { |
| | |
| | | && stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() == 0 |
| | | ) { |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), wrkMast.getStaNo(), 0); |
| | | Integer moveStaNo = wrkMast.getStaNo(); |
| | | |
| | | if (!outOrderList.isEmpty()) { |
| | | List<NavigateNode> nodes = navigateUtils.calcByStationId(stationProtocol.getStationId(), wrkMast.getStaNo()); |
| | | for (int i = nodes.size() - 1; i >= 0; i--) { |
| | | NavigateNode node = nodes.get(i); |
| | | JSONObject v = JSONObject.parseObject(node.getNodeValue()); |
| | | if (v != null) { |
| | | Integer stationId = v.getInteger("stationId"); |
| | | if (outOrderList.contains(stationId)) { |
| | | moveStaNo = stationId; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), moveStaNo, loadGuardState); |
| | | |
| | | if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) { |
| | | return; |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), moveStaNo, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | |
| | | MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command)); |
| | | News.info("输送站点出库命令下发成功,站点号={},工作号={},命令数据={}", stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command)); |
| | | redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5); |
| | | redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_COMPLETE_LIMIT.key + wrkMast.getWrkNo(), "lock", 60); |
| | | redisUtil.del(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + wrkMast.getWrkNo()); |
| | | currentStationTaskCountRef[0]++; |
| | | loadGuardState.reserveLoopTask(loopHitResult.getLoopNo()); |
| | | saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult); |
| | | } |
| | | } |
| | | } |
| | |
| | | notifyUtils.notify(String.valueOf(SlaveType.Devp), stationObjModel.getDeviceNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.STATION_OUT_TASK_RUN, null); |
| | | News.info("输送站点出库命令下发成功,站点号={},工作号={},命令数据={}", stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command)); |
| | | redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5); |
| | | redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_COMPLETE_LIMIT.key + wrkMast.getWrkNo(), "lock", 60); |
| | | redisUtil.del(RedisKeyType.DUAL_CRN_OUT_TASK_STATION_INFO.key + wrkMast.getWrkNo()); |
| | | } |
| | | } |
| | |
| | | List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().eq("wrk_sts", WrkStsType.STATION_RUN.sts)); |
| | | for (WrkMast wrkMast : wrkMasts) { |
| | | Integer wrkNo = wrkMast.getWrkNo(); |
| | | Integer targetStaNo = wrkMast.getStaNo(); |
| | | |
| | | boolean complete = false; |
| | | BasStation basStation = basStationService.selectOne(new EntityWrapper<BasStation>().eq("station_id", targetStaNo)); |
| | | if (basStation == null) { |
| | | continue; |
| | | } |
| | | |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap(); |
| | | StationProtocol stationProtocol = statusMap.get(basStation.getStationId()); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (stationProtocol.getTaskNo().equals(wrkNo)) { |
| | | complete = true; |
| | | } |
| | | |
| | | if (complete) { |
| | | wrkMast.setWrkSts(WrkStsType.STATION_RUN_COMPLETE.sts); |
| | | wrkMast.setIoTime(new Date()); |
| | | wrkMastService.updateById(wrkMast); |
| | | notifyUtils.notify(String.valueOf(SlaveType.Devp), basStation.getDeviceNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.STATION_OUT_TASK_RUN_COMPLETE, null); |
| | | redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_COMPLETE_LIMIT.key + wrkMast.getWrkNo(), "lock", 60); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | // 检测任务转完成 |
| | | public synchronized void checkTaskToComplete() { |
| | | try { |
| | | List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>().eq("wrk_sts", WrkStsType.STATION_RUN_COMPLETE.sts)); |
| | | for (WrkMast wrkMast : wrkMasts) { |
| | | Integer wrkNo = wrkMast.getWrkNo(); |
| | | Integer targetStaNo = wrkMast.getStaNo(); |
| | | |
| | | Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_COMPLETE_LIMIT.key + wrkNo); |
| | | if (lock != null) { |
| | | continue; |
| | | } |
| | | |
| | | boolean complete = true; |
| | | List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | boolean complete = false; |
| | | BasStation basStation = basStationService.selectOne(new EntityWrapper<BasStation>().eq("station_id", targetStaNo)); |
| | | if (basStation == null) { |
| | | continue; |
| | | } |
| | | |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | List<StationProtocol> list = stationThread.getStatus(); |
| | | for (StationProtocol stationProtocol : list) { |
| | | if (stationProtocol.getTaskNo().equals(wrkNo)) { |
| | | complete = false; |
| | | Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap(); |
| | | StationProtocol stationProtocol = statusMap.get(basStation.getStationId()); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | if (!stationProtocol.getTaskNo().equals(wrkNo)) { |
| | | complete = true; |
| | | } |
| | | |
| | | if (complete) { |
| | |
| | | |
| | | //获取输送线任务数量 |
| | | public synchronized int getCurrentStationTaskCount() { |
| | | int currentStationTaskCount = 0; |
| | | return countCurrentStationTask(); |
| | | } |
| | | |
| | | // 检测出库排序 |
| | | public synchronized void checkStationOutOrder() { |
| | | List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<BasDevp>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getId()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap(); |
| | | List<StationObjModel> orderList = basDevp.getOutOrderList$(); |
| | | for (StationObjModel stationObjModel : orderList) { |
| | | StationProtocol stationProtocol = statusMap.get(stationObjModel.getStationId()); |
| | | if (stationProtocol == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (!stationProtocol.isAutoing()) { |
| | | continue; |
| | | } |
| | | |
| | | if (!stationProtocol.isLoading()) { |
| | | continue; |
| | | } |
| | | |
| | | if (stationProtocol.getTaskNo() <= 0) { |
| | | continue; |
| | | } |
| | | |
| | | if (!stationProtocol.getStationId().equals(stationProtocol.getTargetStaNo())) { |
| | | continue; |
| | | } |
| | | |
| | | WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo()); |
| | | if (wrkMast == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (Cools.isEmpty(wrkMast.getBatch())) { |
| | | continue; |
| | | } |
| | | |
| | | if (Cools.isEmpty(wrkMast.getBatchSeq())) { |
| | | continue; |
| | | } |
| | | |
| | | List<WrkMast> batchWrkList = wrkMastService.selectList(new EntityWrapper<WrkMast>() |
| | | .notIn("wrk_sts", WrkStsType.STATION_RUN_COMPLETE.sts, WrkStsType.COMPLETE_OUTBOUND.sts) |
| | | .eq("batch", wrkMast.getBatch()) |
| | | .orderBy("batch") |
| | | ); |
| | | if (batchWrkList.isEmpty()) { |
| | | continue; |
| | | } |
| | | WrkMast firstWrkMast = batchWrkList.get(0); |
| | | Integer currentBatchSeq = firstWrkMast.getBatchSeq(); |
| | | |
| | | List<NavigateNode> initPath = navigateUtils.calcByStationId(wrkMast.getSourceStaNo(), wrkMast.getStaNo()); |
| | | |
| | | String commandType = "none"; |
| | | Integer seq = getOutStationBatchSeq(initPath, stationProtocol.getStationId(), wrkMast.getBatch()); |
| | | if (seq == null) { |
| | | if (currentBatchSeq.equals(wrkMast.getBatchSeq())) { |
| | | commandType = "toTarget"; |
| | | }else { |
| | | commandType = "toCircle"; |
| | | } |
| | | }else { |
| | | seq++; |
| | | if (seq.equals(wrkMast.getBatchSeq()) && currentBatchSeq.equals(wrkMast.getBatchSeq())) { |
| | | commandType = "toTarget"; |
| | | }else { |
| | | commandType = "toCircle"; |
| | | } |
| | | } |
| | | |
| | | if (commandType.equals("toTarget")) { |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), wrkMast.getStaNo(), 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command)); |
| | | News.info("{}任务直接去目标点", wrkMast.getWrkNo()); |
| | | } else if (commandType.equals("toCircle")) { |
| | | Integer circleTarget = null; |
| | | for (NavigateNode node : initPath) { |
| | | JSONObject v = JSONObject.parseObject(node.getNodeValue()); |
| | | if (v != null) { |
| | | Integer stationId = v.getInteger("stationId"); |
| | | try { |
| | | List<NavigateNode> enableMovePath = navigateUtils.calcByStationId(stationProtocol.getStationId(), stationId); |
| | | if (enableMovePath.isEmpty()) { |
| | | continue; |
| | | } |
| | | } catch (Exception e) { |
| | | continue; |
| | | } |
| | | |
| | | circleTarget = stationId; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (circleTarget == null) { |
| | | continue; |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), circleTarget, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command)); |
| | | redisUtil.set(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkMast.getWrkNo(), JSON.toJSONString(command, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24); |
| | | News.info("{}任务进行绕圈", wrkMast.getWrkNo()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 监控绕圈站点 |
| | | public synchronized void watchCircleStation() { |
| | | List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<BasDevp>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getId()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | List<Integer> outOrderList = basDevp.getOutOrderIntList(); |
| | | |
| | | for (StationProtocol stationProtocol : stationThread.getStatus()) { |
| | | if (!stationProtocol.isAutoing()) { |
| | | continue; |
| | | } |
| | | |
| | | if (!stationProtocol.isLoading()) { |
| | | continue; |
| | | } |
| | | |
| | | if (stationProtocol.getTaskNo() <= 0) { |
| | | continue; |
| | | } |
| | | |
| | | Object circleObj = redisUtil.get(RedisKeyType.WATCH_CIRCLE_STATION_.key + stationProtocol.getTaskNo()); |
| | | if (circleObj == null) { |
| | | continue; |
| | | } |
| | | |
| | | StationCommand circleCommand = JSON.parseObject(circleObj.toString(), StationCommand.class); |
| | | if (!stationProtocol.getStationId().equals(circleCommand.getTargetStaNo())) { |
| | | continue; |
| | | } |
| | | |
| | | WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo()); |
| | | if (wrkMast == null) { |
| | | continue; |
| | | } |
| | | |
| | | Integer moveStaNo = wrkMast.getStaNo(); |
| | | |
| | | if (!outOrderList.isEmpty()) { |
| | | List<NavigateNode> nodes = navigateUtils.calcByStationId(stationProtocol.getStationId(), wrkMast.getStaNo()); |
| | | for (int i = nodes.size() - 1; i >= 0; i--) { |
| | | NavigateNode node = nodes.get(i); |
| | | JSONObject v = JSONObject.parseObject(node.getNodeValue()); |
| | | if (v != null) { |
| | | Integer stationId = v.getInteger("stationId"); |
| | | if (outOrderList.contains(stationId)) { |
| | | moveStaNo = stationId; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), moveStaNo, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public List<Integer> getAllOutOrderList() { |
| | | List<Integer> list = new ArrayList<>(); |
| | | List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<BasDevp>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | List<Integer> orderList = basDevp.getOutOrderIntList(); |
| | | list.addAll(orderList); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | public Integer getOutStationBatchSeq(List<NavigateNode> pathList, Integer searchStationId, String searchBatch) { |
| | | List<Integer> checkList = new ArrayList<>(); |
| | | for (int i = pathList.size() - 1; i >= 0; i--) { |
| | | NavigateNode node = pathList.get(i); |
| | | JSONObject v = JSONObject.parseObject(node.getNodeValue()); |
| | | if (v != null) { |
| | | Integer stationId = v.getInteger("stationId"); |
| | | if (searchStationId.equals(stationId)) { |
| | | break; |
| | | } else { |
| | | checkList.add(stationId); |
| | | } |
| | | } |
| | | } |
| | | |
| | | HashMap<String, Integer> batchMap = new HashMap<>(); |
| | | for (Integer station : checkList) { |
| | | BasStation basStation = basStationService.selectOne(new EntityWrapper<BasStation>().eq("station_id", station)); |
| | | if (basStation == null) { |
| | | continue; |
| | | } |
| | | |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap(); |
| | | StationProtocol checkStationProtocol = statusMap.get(station); |
| | | if (checkStationProtocol == null) { |
| | | continue; |
| | | } |
| | | if (checkStationProtocol.getTaskNo() > 0) { |
| | | WrkMast checkWrkMast = wrkMastService.selectByWorkNo(checkStationProtocol.getTaskNo()); |
| | | if (checkWrkMast == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (!Cools.isEmpty(checkWrkMast.getBatch())) { |
| | | batchMap.put(checkWrkMast.getBatch(), checkWrkMast.getBatchSeq()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | Integer seq = batchMap.get(searchBatch); |
| | | return seq; |
| | | } |
| | | |
| | | private int countCurrentStationTask() { |
| | | int currentStationTaskCount = 0; |
| | | List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<BasDevp>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | return currentStationTaskCount; |
| | | } |
| | | |
| | | private boolean isDispatchBlocked(DispatchLimitConfig config, |
| | | int currentStationTaskCount, |
| | | LoadGuardState loadGuardState, |
| | | boolean needReserveLoopLoad) { |
| | | if (config.loopModeEnable) { |
| | | double currentLoad = loadGuardState.currentLoad(); |
| | | if (currentLoad >= config.circleMaxLoadLimit) { |
| | | News.warn("当前承载量达到上限,已停止站点任务下发。当前承载量={},上限={}", formatPercent(currentLoad), formatPercent(config.circleMaxLoadLimit)); |
| | | return true; |
| | | } |
| | | |
| | | if (needReserveLoopLoad) { |
| | | double reserveLoad = loadGuardState.loadAfterReserve(); |
| | | if (reserveLoad >= config.circleMaxLoadLimit) { |
| | | News.warn("预占后承载量达到上限,已停止站点任务下发。预占后承载量={},上限={}", formatPercent(reserveLoad), formatPercent(config.circleMaxLoadLimit)); |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private LoadGuardState buildLoadGuardState(DispatchLimitConfig config) { |
| | | LoadGuardState state = new LoadGuardState(); |
| | | if (!config.loopModeEnable) { |
| | | return state; |
| | | } |
| | | |
| | | StationCycleCapacityVo capacityVo = stationCycleCapacityService.getLatestSnapshot(); |
| | | if (capacityVo == null) { |
| | | return state; |
| | | } |
| | | |
| | | state.totalStationCount = toNonNegative(capacityVo.getTotalStationCount()); |
| | | state.projectedTaskStationCount = toNonNegative(capacityVo.getTaskStationCount()); |
| | | |
| | | List<StationCycleLoopVo> loopList = capacityVo.getLoopList(); |
| | | if (loopList != null) { |
| | | for (StationCycleLoopVo loopVo : loopList) { |
| | | if (loopVo == null || loopVo.getStationIdList() == null) { |
| | | continue; |
| | | } |
| | | Integer loopNo = loopVo.getLoopNo(); |
| | | for (Integer stationId : loopVo.getStationIdList()) { |
| | | if (stationId != null) { |
| | | if (loopNo != null) { |
| | | state.stationLoopNoMap.put(stationId, loopNo); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return state; |
| | | } |
| | | |
| | | private LoopHitResult findPathLoopHit(DispatchLimitConfig config, |
| | | Integer sourceStationId, |
| | | Integer targetStationId, |
| | | LoadGuardState loadGuardState) { |
| | | if (!config.loopModeEnable) { |
| | | return LoopHitResult.NO_HIT; |
| | | } |
| | | if (sourceStationId == null || targetStationId == null) { |
| | | return LoopHitResult.NO_HIT; |
| | | } |
| | | if (loadGuardState.stationLoopNoMap.isEmpty()) { |
| | | return LoopHitResult.NO_HIT; |
| | | } |
| | | |
| | | try { |
| | | List<NavigateNode> nodes = navigateUtils.calcByStationId(sourceStationId, targetStationId); |
| | | if (nodes == null || nodes.isEmpty()) { |
| | | return LoopHitResult.NO_HIT; |
| | | } |
| | | |
| | | for (NavigateNode node : nodes) { |
| | | Integer stationId = getStationIdFromNode(node); |
| | | if (stationId == null) { |
| | | continue; |
| | | } |
| | | Integer loopNo = loadGuardState.stationLoopNoMap.get(stationId); |
| | | if (loopNo != null) { |
| | | return new LoopHitResult(true, loopNo, stationId); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | return LoopHitResult.NO_HIT; |
| | | } |
| | | |
| | | return LoopHitResult.NO_HIT; |
| | | } |
| | | |
| | | private Integer getStationIdFromNode(NavigateNode node) { |
| | | if (node == null || isBlank(node.getNodeValue())) { |
| | | return null; |
| | | } |
| | | try { |
| | | JSONObject v = JSONObject.parseObject(node.getNodeValue()); |
| | | if (v == null) { |
| | | return null; |
| | | } |
| | | return v.getInteger("stationId"); |
| | | } catch (Exception e) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private int toNonNegative(Integer value) { |
| | | if (value == null || value < 0) { |
| | | return 0; |
| | | } |
| | | return value; |
| | | } |
| | | |
| | | private void saveLoopLoadReserve(Integer wrkNo, LoopHitResult loopHitResult) { |
| | | if (wrkNo == null || wrkNo <= 0 || loopHitResult == null || !loopHitResult.isThroughLoop()) { |
| | | return; |
| | | } |
| | | JSONObject reserveJson = new JSONObject(); |
| | | reserveJson.put("wrkNo", wrkNo); |
| | | reserveJson.put("loopNo", loopHitResult.getLoopNo()); |
| | | reserveJson.put("hitStationId", loopHitResult.getHitStationId()); |
| | | reserveJson.put("createTime", System.currentTimeMillis()); |
| | | redisUtil.hset(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, String.valueOf(wrkNo), reserveJson.toJSONString()); |
| | | redisUtil.expire(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, LOOP_LOAD_RESERVE_EXPIRE_SECONDS); |
| | | } |
| | | |
| | | private DispatchLimitConfig getDispatchLimitConfig() { |
| | | DispatchLimitConfig config = new DispatchLimitConfig(); |
| | | Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (!(systemConfigMapObj instanceof Map)) { |
| | | return config; |
| | | } |
| | | Map<?, ?> systemConfigMap = (Map<?, ?>) systemConfigMapObj; |
| | | |
| | | config.circleMaxLoadLimit = parseLoadLimit(getConfigValue(systemConfigMap, "circleMaxLoadLimit"), config.circleMaxLoadLimit); |
| | | String loopModeValue = getConfigValue(systemConfigMap, "circleLoopModeEnable"); |
| | | if (isBlank(loopModeValue)) { |
| | | loopModeValue = getConfigValue(systemConfigMap, "circleModeEnable"); |
| | | } |
| | | if (isBlank(loopModeValue)) { |
| | | loopModeValue = getConfigValue(systemConfigMap, "isCircleMode"); |
| | | } |
| | | config.loopModeEnable = parseBoolean(loopModeValue, config.loopModeEnable); |
| | | |
| | | return config; |
| | | } |
| | | |
| | | private String getConfigValue(Map<?, ?> configMap, String key) { |
| | | Object value = configMap.get(key); |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | return String.valueOf(value).trim(); |
| | | } |
| | | |
| | | private boolean parseBoolean(String value, boolean defaultValue) { |
| | | if (isBlank(value)) { |
| | | return defaultValue; |
| | | } |
| | | String lowValue = value.toLowerCase(Locale.ROOT); |
| | | if ("y".equals(lowValue) || "yes".equals(lowValue) || "true".equals(lowValue) |
| | | || "1".equals(lowValue) || "on".equals(lowValue)) { |
| | | return true; |
| | | } |
| | | if ("n".equals(lowValue) || "no".equals(lowValue) || "false".equals(lowValue) |
| | | || "0".equals(lowValue) || "off".equals(lowValue)) { |
| | | return false; |
| | | } |
| | | return defaultValue; |
| | | } |
| | | |
| | | private double parseLoadLimit(String value, double defaultValue) { |
| | | if (isBlank(value)) { |
| | | return defaultValue; |
| | | } |
| | | try { |
| | | String normalized = value.replace("%", "").trim(); |
| | | double parsed = Double.parseDouble(normalized); |
| | | if (parsed > 1.0) { |
| | | parsed = parsed / 100.0; |
| | | } |
| | | if (parsed < 0.0) { |
| | | return 0.0; |
| | | } |
| | | if (parsed > 1.0) { |
| | | return 1.0; |
| | | } |
| | | return parsed; |
| | | } catch (Exception e) { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | private int parseInt(String value, int defaultValue) { |
| | | if (isBlank(value)) { |
| | | return defaultValue; |
| | | } |
| | | try { |
| | | int parsed = Integer.parseInt(value.trim()); |
| | | return parsed < 0 ? defaultValue : parsed; |
| | | } catch (Exception e) { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | private String formatPercent(double value) { |
| | | return String.format(Locale.ROOT, "%.1f%%", value * 100.0); |
| | | } |
| | | |
| | | private boolean isBlank(String value) { |
| | | return value == null || value.trim().isEmpty(); |
| | | } |
| | | |
| | | private static class DispatchLimitConfig { |
| | | // 圈最大承载能力,默认80% |
| | | private double circleMaxLoadLimit = 0.8d; |
| | | // 是否启用绕圈模式(仅启用时才生效承载限制) |
| | | private boolean loopModeEnable = false; |
| | | } |
| | | |
| | | private static class LoadGuardState { |
| | | private int totalStationCount = 0; |
| | | private int projectedTaskStationCount = 0; |
| | | private final Map<Integer, Integer> stationLoopNoMap = new HashMap<>(); |
| | | |
| | | private double currentLoad() { |
| | | return calcLoad(this.projectedTaskStationCount, this.totalStationCount); |
| | | } |
| | | |
| | | private double loadAfterReserve() { |
| | | return calcLoad(this.projectedTaskStationCount + 1, this.totalStationCount); |
| | | } |
| | | |
| | | private void reserveLoopTask(Integer loopNo) { |
| | | if (loopNo == null || loopNo <= 0) { |
| | | return; |
| | | } |
| | | if (this.totalStationCount <= 0) { |
| | | return; |
| | | } |
| | | this.projectedTaskStationCount++; |
| | | } |
| | | |
| | | private double calcLoad(int taskCount, int stationCount) { |
| | | if (stationCount <= 0 || taskCount <= 0) { |
| | | return 0.0; |
| | | } |
| | | double load = (double) taskCount / (double) stationCount; |
| | | if (load < 0.0) { |
| | | return 0.0; |
| | | } |
| | | if (load > 1.0) { |
| | | return 1.0; |
| | | } |
| | | return load; |
| | | } |
| | | } |
| | | |
| | | private static class LoopHitResult { |
| | | private static final LoopHitResult NO_HIT = new LoopHitResult(false, null, null); |
| | | private final boolean throughLoop; |
| | | private final Integer loopNo; |
| | | private final Integer hitStationId; |
| | | |
| | | private LoopHitResult(boolean throughLoop, Integer loopNo, Integer hitStationId) { |
| | | this.throughLoop = throughLoop; |
| | | this.loopNo = loopNo; |
| | | this.hitStationId = hitStationId; |
| | | } |
| | | |
| | | private boolean isThroughLoop() { |
| | | return throughLoop; |
| | | } |
| | | |
| | | private Integer getLoopNo() { |
| | | return loopNo; |
| | | } |
| | | |
| | | private Integer getHitStationId() { |
| | | return hitStationId; |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | package com.zy.system.controller; |
| | | |
| | | import com.core.common.Cools; |
| | | import com.core.common.R; |
| | | import com.zy.system.entity.license.*; |
| | | import com.zy.system.timer.LicenseTimer; |
| | |
| | | */ |
| | | @RequestMapping(value = "/getServerInfos",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) |
| | | public LicenseCheck getServerInfos(@RequestParam(value = "osName",required = false) String osName) { |
| | | //操作系统类型 |
| | | if(Cools.isEmpty(osName)){ |
| | | osName = System.getProperty("os.name"); |
| | | } |
| | | osName = osName.toLowerCase(); |
| | | |
| | | AbstractServerInfos abstractServerInfos = null; |
| | | |
| | | //根据不同操作系统类型选择不同的数据获取方法 |
| | | if (osName.startsWith("windows")) { |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } else if (osName.startsWith("linux")) { |
| | | abstractServerInfos = new LinuxServerInfos(); |
| | | }else{//其他服务器类型 |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } |
| | | |
| | | return abstractServerInfos.getServerInfos(); |
| | | return LicenseUtils.getServerInfos(); |
| | | } |
| | | |
| | | /** |
| | |
| | | //License中可被允许的参数信息 |
| | | LicenseCheck expectedCheckModel = (LicenseCheck) content.getExtra(); |
| | | //当前服务器真实的参数信息 |
| | | LicenseCheck serverCheckModel = getServerInfos(); |
| | | LicenseCheck serverCheckModel = LicenseUtils.getServerInfos(); |
| | | |
| | | if(expectedCheckModel != null && serverCheckModel != null){ |
| | | //校验IP地址 |
| | |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 获取当前服务器需要额外校验的License参数 |
| | | */ |
| | | private LicenseCheck getServerInfos(){ |
| | | //操作系统类型 |
| | | String osName = System.getProperty("os.name").toLowerCase(); |
| | | AbstractServerInfos abstractServerInfos = null; |
| | | |
| | | //根据不同操作系统类型选择不同的数据获取方法 |
| | | if (osName.startsWith("windows")) { |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } else if (osName.startsWith("linux")) { |
| | | abstractServerInfos = new LinuxServerInfos(); |
| | | }else{//其他服务器类型 |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } |
| | | |
| | | return abstractServerInfos.getServerInfos(); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | if (inetAddresses != null && inetAddresses.size() > 0) { |
| | | //2. 获取所有网络接口的Mac地址 |
| | | result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList()); |
| | | result = inetAddresses.stream() |
| | | .map(this::getMacByInetAddress) |
| | | .filter(mac -> mac != null && !mac.trim().isEmpty()) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | return result; |
| | |
| | | |
| | | if(inetAddresses != null && inetAddresses.size() > 0){ |
| | | //2. 获取所有网络接口的Mac地址 |
| | | result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList()); |
| | | result = inetAddresses.stream() |
| | | .map(this::getMacByInetAddress) |
| | | .filter(mac -> mac != null && !mac.trim().isEmpty()) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | return result; |
| | |
| | | |
| | | public void getRemoteLicense() { |
| | | try { |
| | | AbstractServerInfos abstractServerInfos = null; |
| | | String osName = System.getProperty("os.name"); |
| | | // 根据不同操作系统类型选择不同的数据获取方法 |
| | | if (osName.startsWith("windows")) { |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } else if (osName.startsWith("linux")) { |
| | | abstractServerInfos = new LinuxServerInfos(); |
| | | } else {// 其他服务器类型 |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } |
| | | LicenseCheck serverInfos = abstractServerInfos.getServerInfos(); |
| | | LicenseCheck serverInfos = LicenseUtils.getServerInfos(); |
| | | |
| | | HashMap<String, Object> map = new HashMap<>(); |
| | | map.put("subject", subject); |
| | |
| | | validation-timeout: 3000 |
| | | connection-test-query: select 1 |
| | | driver-class-name: com.mysql.jdbc.Driver |
| | | url: jdbc:mysql://127.0.0.1:3306/wcs?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai |
| | | url: jdbc:mysql://192.168.238.31:3306/wcs?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai |
| | | username: root |
| | | password: root |
| | | password: zy@123 |
| | | mvc: |
| | | static-path-pattern: /** |
| | | redis: |
| | |
| | | |
| | | #License相关配置 |
| | | license: |
| | | subject: crnWcsDev |
| | | subject: jsxswcs1 |
| | | publicAlias: publicCert |
| | | storePass: public_zhongyang_123456789 |
| | | licensePath: license.lic |
| | |
| | | threadControlCount: 10 |
| | | liftType: lift |
| | | |
| | | mainProcessPlugin: FakeProcess |
| | | mainProcessPlugin: XiaosongProcess |
| | | |
| | | deviceLogStorage: |
| | | # 设备日志存储方式 mysql file |
| | |
| | | <el-descriptions-item label="运行阻塞">{{ item.runBlock ? 'Y' : 'N' }}</el-descriptions-item> |
| | | <el-descriptions-item label="启动入库">{{ item.enableIn ? 'Y' : 'N' }}</el-descriptions-item> |
| | | <el-descriptions-item label="托盘高度">{{ item.palletHeight }}</el-descriptions-item> |
| | | <el-descriptions-item label="条码">{{ item.barcode }}</el-descriptions-item> |
| | | <el-descriptions-item label="条码"> |
| | | <el-popover v-if="item.barcode" placement="top" width="460" trigger="hover"> |
| | | <div style="text-align: center;"> |
| | | <img |
| | | :src="getBarcodePreview(item.barcode)" |
| | | :alt="'barcode-' + item.barcode" |
| | | style="display: block; max-width: 100%; height: auto; margin: 0 auto; image-rendering: pixelated; background: #fff;" |
| | | /> |
| | | <div style="margin-top: 4px; font-size: 12px; word-break: break-all;">{{ item.barcode }}</div> |
| | | </div> |
| | | <span slot="reference" @click.stop="handleBarcodeClick(item)" style="cursor: pointer; color: #409EFF;">{{ item.barcode }}</span> |
| | | </el-popover> |
| | | <span v-else @click.stop="handleBarcodeClick(item)" style="cursor: pointer; color: #409EFF;">-</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="重量">{{ item.weight }}</el-descriptions-item> |
| | | <el-descriptions-item label="任务可写区">{{ item.taskWriteIdx }}</el-descriptions-item> |
| | | <el-descriptions-item label="故障代码">{{ item.error }}</el-descriptions-item> |
| | | <el-descriptions-item label="故障信息">{{ item.errorMsg }}</el-descriptions-item> |
| | | <el-descriptions-item label="扩展数据">{{ item.extend }}</el-descriptions-item> |
| | |
| | | taskNo: "", |
| | | targetStationId: "", |
| | | }, |
| | | barcodePreviewCache: {}, |
| | | pageSize: 25, |
| | | currentPage: 1, |
| | | timer: null |
| | |
| | | this.pageSize = size; |
| | | this.currentPage = 1; |
| | | }, |
| | | getBarcodePreview(barcode) { |
| | | const value = String(barcode || "").trim(); |
| | | if (!value) { |
| | | return ""; |
| | | } |
| | | if (this.barcodePreviewCache[value]) { |
| | | return this.barcodePreviewCache[value]; |
| | | } |
| | | const encodeResult = this.encodeCode128(value); |
| | | if (!encodeResult) { |
| | | return ""; |
| | | } |
| | | const svg = this.buildCode128Svg(encodeResult, value); |
| | | const dataUrl = "data:image/svg+xml;charset=UTF-8," + encodeURIComponent(svg); |
| | | this.$set(this.barcodePreviewCache, value, dataUrl); |
| | | return dataUrl; |
| | | }, |
| | | encodeCode128(value) { |
| | | if (!value) { |
| | | return null; |
| | | } |
| | | const isNumeric = /^\d+$/.test(value); |
| | | if (isNumeric && value.length % 2 === 0) { |
| | | return this.encodeCode128C(value); |
| | | } |
| | | return this.encodeCode128B(value); |
| | | }, |
| | | encodeCode128B(value) { |
| | | const codes = [104]; |
| | | for (let i = 0; i < value.length; i++) { |
| | | const code = value.charCodeAt(i) - 32; |
| | | if (code < 0 || code > 94) { |
| | | return null; |
| | | } |
| | | codes.push(code); |
| | | } |
| | | return this.buildCode128Pattern(codes); |
| | | }, |
| | | encodeCode128C(value) { |
| | | if (value.length % 2 !== 0) { |
| | | return null; |
| | | } |
| | | const codes = [105]; |
| | | for (let i = 0; i < value.length; i += 2) { |
| | | codes.push(parseInt(value.substring(i, i + 2), 10)); |
| | | } |
| | | return this.buildCode128Pattern(codes); |
| | | }, |
| | | buildCode128Pattern(codes) { |
| | | const patterns = this.getCode128Patterns(); |
| | | let checksum = codes[0]; |
| | | for (let i = 1; i < codes.length; i++) { |
| | | checksum += codes[i] * i; |
| | | } |
| | | const checkCode = checksum % 103; |
| | | const fullCodes = codes.concat([checkCode, 106]); |
| | | let bars = ""; |
| | | for (let i = 0; i < fullCodes.length; i++) { |
| | | const code = fullCodes[i]; |
| | | if (patterns[code] == null) { |
| | | return null; |
| | | } |
| | | bars += patterns[code]; |
| | | } |
| | | bars += "11"; |
| | | return bars; |
| | | }, |
| | | buildCode128Svg(bars, text) { |
| | | const quietModules = 20; |
| | | const modules = quietModules * 2 + bars.split("").reduce((sum, n) => sum + parseInt(n, 10), 0); |
| | | const moduleWidth = modules > 300 ? 1 : 2; |
| | | const width = modules * moduleWidth; |
| | | const barTop = 10; |
| | | const barHeight = 110; |
| | | let x = quietModules * moduleWidth; |
| | | let black = true; |
| | | let rects = ""; |
| | | for (let i = 0; i < bars.length; i++) { |
| | | const w = parseInt(bars[i], 10) * moduleWidth; |
| | | if (black) { |
| | | rects += '<rect x="' + x + '" y="' + barTop + '" width="' + w + '" height="' + barHeight + '" fill="#000" shape-rendering="crispEdges" />'; |
| | | } |
| | | x += w; |
| | | black = !black; |
| | | } |
| | | return ( |
| | | '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="145" viewBox="0 0 ' + width + ' 145">' + |
| | | '<rect width="100%" height="100%" fill="#fff" />' + |
| | | rects + |
| | | '<text x="' + (width / 2) + '" y="136" text-anchor="middle" font-family="monospace" font-size="14" fill="#111">' + |
| | | this.escapeXml(text) + |
| | | "</text>" + |
| | | "</svg>" |
| | | ); |
| | | }, |
| | | getCode128Patterns() { |
| | | return [ |
| | | "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", |
| | | "221312", "231212", "112232", "122132", "122231", "113222", "123122", "123221", "223211", "221132", |
| | | "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", |
| | | "212123", "212321", "232121", "111323", "131123", "131321", "112313", "132113", "132311", "211313", |
| | | "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", |
| | | "231131", "213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", |
| | | "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214", |
| | | "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", |
| | | "111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112", "421211", "212141", |
| | | "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", |
| | | "114131", "311141", "411131", "211412", "211214", "211232", "2331112" |
| | | ]; |
| | | }, |
| | | escapeXml(text) { |
| | | return String(text) |
| | | .replace(/&/g, "&") |
| | | .replace(/</g, "<") |
| | | .replace(/>/g, ">") |
| | | .replace(/"/g, """) |
| | | .replace(/'/g, "'"); |
| | | }, |
| | | getDevpStateInfo() { |
| | | if (this.readOnly) { |
| | | // Frontend filtering for readOnly mode |
| | |
| | | } |
| | | } |
| | | }, |
| | | handleBarcodeClick(item) { |
| | | if (this.readOnly || !item || item.stationId == null) { |
| | | return; |
| | | } |
| | | |
| | | let that = this; |
| | | $.ajax({ |
| | | url: baseUrl + "/openapi/getFakeSystemRunStatus", |
| | | headers: { |
| | | token: localStorage.getItem("token"), |
| | | }, |
| | | method: "get", |
| | | success: (res) => { |
| | | if (res.code !== 200 || !res.data || !res.data.isFake || !res.data.running) { |
| | | that.$message({ |
| | | message: "仅仿真模式运行中可修改条码", |
| | | type: "warning", |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | that.$prompt("请输入新的条码值(可留空清空)", "修改条码", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | inputValue: item.barcode || "", |
| | | inputPlaceholder: "请输入条码", |
| | | }).then(({ value }) => { |
| | | that.updateStationBarcode(item.stationId, value == null ? "" : String(value).trim()); |
| | | }).catch(() => {}); |
| | | }, |
| | | }); |
| | | }, |
| | | updateStationBarcode(stationId, barcode) { |
| | | let that = this; |
| | | $.ajax({ |
| | | url: baseUrl + "/station/command/barcode", |
| | | headers: { |
| | | token: localStorage.getItem("token"), |
| | | }, |
| | | contentType: "application/json", |
| | | method: "post", |
| | | data: JSON.stringify({ |
| | | stationId: stationId, |
| | | barcode: barcode, |
| | | }), |
| | | success: (res) => { |
| | | if (res.code == 200) { |
| | | that.syncLocalBarcode(stationId, barcode); |
| | | that.$message({ |
| | | message: "条码修改成功", |
| | | type: "success", |
| | | }); |
| | | } else { |
| | | that.$message({ |
| | | message: res.msg || "条码修改失败", |
| | | type: "warning", |
| | | }); |
| | | } |
| | | }, |
| | | }); |
| | | }, |
| | | syncLocalBarcode(stationId, barcode) { |
| | | let updateFn = (list) => { |
| | | if (!list || list.length === 0) { |
| | | return; |
| | | } |
| | | list.forEach((row) => { |
| | | if (row.stationId == stationId) { |
| | | row.barcode = barcode; |
| | | } |
| | | }); |
| | | }; |
| | | updateFn(this.stationList); |
| | | updateFn(this.fullStationList); |
| | | }, |
| | | openControl() { |
| | | this.showControl = !this.showControl; |
| | | }, |
| | |
| | | Vue.component('map-canvas', { |
| | | template: ` |
| | | <div style="width: 100%; height: 100%; position: relative;"> |
| | | <div ref="pixiView"></div> |
| | | <div ref="pixiView" style="position: absolute; inset: 0;"></div> |
| | | <div style="position: absolute; top: 12px; left: 14px; z-index: 30; pointer-events: none; max-width: 52%;"> |
| | | <div style="display: flex; flex-direction: column; gap: 6px; align-items: flex-start;"> |
| | | <div v-for="item in cycleCapacity.loopList" |
| | | :key="'loop-' + item.loopNo" |
| | | @mouseenter="handleLoopCardEnter(item)" |
| | | @mouseleave="handleLoopCardLeave(item)" |
| | | style="padding: 6px 10px; border-radius: 4px; background: rgba(11, 35, 58, 0.72); color: #fff; font-size: 12px; line-height: 1.4; white-space: nowrap; pointer-events: auto;"> |
| | | 圈{{ item.loopNo }} | |
| | | 站点: {{ item.stationCount || 0 }} | |
| | | 任务: {{ item.taskCount || 0 }} | |
| | | 承载: {{ formatLoadPercent(item.currentLoad) }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div v-show="shelfTooltip.visible" |
| | | :style="shelfTooltipStyle()"> |
| | | {{ shelfTooltip.text }} |
| | |
| | | item: null |
| | | }, |
| | | shelfTooltipMinScale: 0.4, |
| | | containerResizeObserver: null, |
| | | timer: null, |
| | | adjustLabelTimer: null, |
| | | isSwitchingFloor: false |
| | | isSwitchingFloor: false, |
| | | cycleCapacity: { |
| | | loopList: [], |
| | | totalStationCount: 0, |
| | | taskStationCount: 0, |
| | | currentLoad: 0 |
| | | }, |
| | | hoverLoopNo: null, |
| | | hoverLoopStationIdSet: new Set(), |
| | | loopHighlightColor: 0xfff34d |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.currentLev = this.lev || 1; |
| | | this.createMap(); |
| | | this.startContainerResizeObserve(); |
| | | this.loadMapTransformConfig(); |
| | | this.loadLocList(); |
| | | this.connectWs(); |
| | |
| | | this.getCrnInfo(); |
| | | this.getDualCrnInfo(); |
| | | this.getSiteInfo(); |
| | | this.getCycleCapacityInfo(); |
| | | this.getRgvInfo(); |
| | | }, 1000); |
| | | }, |
| | |
| | | |
| | | if (this.hoverRaf) { cancelAnimationFrame(this.hoverRaf); this.hoverRaf = null; } |
| | | if (this.pixiApp) { this.pixiApp.destroy(true, { children: true }); } |
| | | if (this.containerResizeObserver) { this.containerResizeObserver.disconnect(); this.containerResizeObserver = null; } |
| | | window.removeEventListener('resize', this.resizeToContainer); |
| | | if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; } |
| | | if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { try { this.ws.close(); } catch (e) {} } |
| | |
| | | }); |
| | | //*******************FPS******************* |
| | | }, |
| | | startContainerResizeObserve() { |
| | | if (typeof ResizeObserver === 'undefined' || !this.$el) { return; } |
| | | this.containerResizeObserver = new ResizeObserver(() => { |
| | | this.resizeToContainer(); |
| | | }); |
| | | this.containerResizeObserver.observe(this.$el); |
| | | }, |
| | | getViewportSize() { |
| | | if (!this.pixiApp || !this.pixiApp.renderer) { return { width: 0, height: 0 }; } |
| | | const screen = this.pixiApp.renderer.screen; |
| | | if (screen && screen.width > 0 && screen.height > 0) { |
| | | return { width: screen.width, height: screen.height }; |
| | | } |
| | | const rect = this.pixiApp.view ? this.pixiApp.view.getBoundingClientRect() : null; |
| | | return { width: rect ? rect.width : 0, height: rect ? rect.height : 0 }; |
| | | }, |
| | | resizeToContainer() { |
| | | const w = this.$el.clientWidth || 0; |
| | | const h = this.$el.clientHeight || 0; |
| | | if (w > 0 && h > 0 && this.pixiApp) { |
| | | const vw = this.pixiApp.renderer && this.pixiApp.renderer.screen ? this.pixiApp.renderer.screen.width : 0; |
| | | const vh = this.pixiApp.renderer && this.pixiApp.renderer.screen ? this.pixiApp.renderer.screen.height : 0; |
| | | if (vw === w && vh === h) { return; } |
| | | this.pixiApp.renderer.resize(w, h); |
| | | if (this.mapContentSize && this.mapContentSize.width > 0 && this.mapContentSize.height > 0) { |
| | | this.applyMapTransform(true); |
| | | } |
| | | } |
| | | }, |
| | | getMap() { |
| | |
| | | }, |
| | | changeFloor(lev) { |
| | | this.currentLev = lev; |
| | | this.clearLoopStationHighlight(); |
| | | this.isSwitchingFloor = true; |
| | | this.hideShelfTooltip(); |
| | | this.hoveredShelfCell = null; |
| | |
| | | this.getMap(); |
| | | }, |
| | | createMapData(map) { |
| | | this.clearLoopStationHighlight(); |
| | | this.hideShelfTooltip(); |
| | | this.hoveredShelfCell = null; |
| | | this.mapRowOffsets = []; |
| | |
| | | sta.statusObj = null; |
| | | if (sta.textObj.parent !== sta) { sta.addChild(sta.textObj); sta.textObj.position.set(sta.width / 2, sta.height / 2); } |
| | | } |
| | | let baseColor = 0xb8b8b8; |
| | | if (status === "site-auto") { |
| | | this.updateColor(sta, 0x78ff81); |
| | | baseColor = 0x78ff81; |
| | | } else if (status === "site-auto-run" || status === "site-auto-id" || status === "site-auto-run-id") { |
| | | this.updateColor(sta, 0xfa51f6); |
| | | baseColor = 0xfa51f6; |
| | | } else if (status === "site-unauto") { |
| | | this.updateColor(sta, 0xb8b8b8); |
| | | baseColor = 0xb8b8b8; |
| | | } else if (status === "machine-pakin") { |
| | | this.updateColor(sta, 0x30bffc); |
| | | baseColor = 0x30bffc; |
| | | } else if (status === "machine-pakout") { |
| | | this.updateColor(sta, 0x97b400); |
| | | baseColor = 0x97b400; |
| | | } else if (status === "site-run-block") { |
| | | this.updateColor(sta, 0xe69138); |
| | | } else { |
| | | this.updateColor(sta, 0xb8b8b8); |
| | | baseColor = 0xe69138; |
| | | } |
| | | this.setStationBaseColor(sta, baseColor); |
| | | }); |
| | | }, |
| | | getCrnInfo() { |
| | |
| | | getRgvInfo() { |
| | | if (this.isSwitchingFloor) { return; } |
| | | this.sendWs(JSON.stringify({ url: "/console/latest/data/rgv", data: {} })); |
| | | }, |
| | | getCycleCapacityInfo() { |
| | | if (this.isSwitchingFloor) { return; } |
| | | this.sendWs(JSON.stringify({ url: "/console/latest/data/station/cycle/capacity", data: {} })); |
| | | }, |
| | | setCycleCapacityInfo(res) { |
| | | const payload = res && res.code === 200 ? res.data : null; |
| | | if (res && res.code === 403) { parent.location.href = baseUrl + "/login"; return; } |
| | | if (!payload) { return; } |
| | | const loopList = Array.isArray(payload.loopList) ? payload.loopList : []; |
| | | this.cycleCapacity = { |
| | | loopList: loopList, |
| | | totalStationCount: payload.totalStationCount || 0, |
| | | taskStationCount: payload.taskStationCount || 0, |
| | | currentLoad: typeof payload.currentLoad === 'number' ? payload.currentLoad : parseFloat(payload.currentLoad || 0) |
| | | }; |
| | | if (this.hoverLoopNo != null) { |
| | | const targetLoop = loopList.find(v => v && v.loopNo === this.hoverLoopNo); |
| | | if (targetLoop) { |
| | | this.hoverLoopStationIdSet = this.buildStationIdSet(targetLoop.stationIdList); |
| | | this.applyLoopStationHighlight(); |
| | | } else { |
| | | this.clearLoopStationHighlight(); |
| | | } |
| | | } |
| | | }, |
| | | formatLoadPercent(load) { |
| | | let value = typeof load === 'number' ? load : parseFloat(load || 0); |
| | | if (!isFinite(value)) { value = 0; } |
| | | if (value < 0) { value = 0; } |
| | | if (value > 1) { value = 1; } |
| | | return (value * 100).toFixed(1) + "%"; |
| | | }, |
| | | setCrnInfo(res) { |
| | | let crns = Array.isArray(res) ? res : (res && res.code === 200 ? res.data : null); |
| | |
| | | if (this.wsReconnectTimer) { clearTimeout(this.wsReconnectTimer); this.wsReconnectTimer = null; } |
| | | this.wsReconnectAttempts = 0; |
| | | this.getMap(this.currentLev); |
| | | this.getCycleCapacityInfo(); |
| | | }, |
| | | webSocketOnError(e) { |
| | | this.scheduleReconnect(); |
| | |
| | | this.setDualCrnInfo(JSON.parse(result.data)); |
| | | } else if (result.url === "/console/latest/data/rgv") { |
| | | this.setRgvInfo(JSON.parse(result.data)); |
| | | } else if (result.url === "/console/latest/data/station/cycle/capacity") { |
| | | this.setCycleCapacityInfo(JSON.parse(result.data)); |
| | | } else if (typeof result.url === "string" && result.url.indexOf("/basMap/lev/") === 0) { |
| | | this.setMap(JSON.parse(result.data)); |
| | | } |
| | |
| | | text.position.set(sprite.width / 2, sprite.height / 2); |
| | | sprite.addChild(text); |
| | | sprite.textObj = text; |
| | | if (siteId != null && siteId !== -1) { this.pixiStaMap.set(parseInt(siteId), sprite); } |
| | | const stationIdInt = parseInt(siteId, 10); |
| | | if (!isNaN(stationIdInt)) { this.pixiStaMap.set(stationIdInt, sprite); } |
| | | sprite._stationId = isNaN(stationIdInt) ? null : stationIdInt; |
| | | sprite._baseColor = 0x00ff7f; |
| | | sprite._loopHighlighted = false; |
| | | sprite.interactive = true; |
| | | sprite.buttonMode = true; |
| | | sprite.on('pointerdown', () => { |
| | |
| | | return; |
| | | } |
| | | sprite.tint = color; |
| | | }, |
| | | setStationBaseColor(sprite, color) { |
| | | if (!sprite) { return; } |
| | | sprite._baseColor = color; |
| | | if (this.isStationInHoverLoop(sprite)) { |
| | | this.applyHighlightColor(sprite); |
| | | } else { |
| | | this.updateColor(sprite, color); |
| | | sprite._loopHighlighted = false; |
| | | } |
| | | }, |
| | | applyHighlightColor(sprite) { |
| | | if (!sprite) { return; } |
| | | this.updateColor(sprite, this.loopHighlightColor); |
| | | sprite._loopHighlighted = true; |
| | | }, |
| | | isStationInHoverLoop(sprite) { |
| | | if (!sprite || sprite._stationId == null || !this.hoverLoopStationIdSet) { return false; } |
| | | return this.hoverLoopStationIdSet.has(sprite._stationId); |
| | | }, |
| | | buildStationIdSet(stationIdList) { |
| | | const set = new Set(); |
| | | if (!Array.isArray(stationIdList)) { return set; } |
| | | stationIdList.forEach((id) => { |
| | | const v = parseInt(id, 10); |
| | | if (!isNaN(v)) { set.add(v); } |
| | | }); |
| | | return set; |
| | | }, |
| | | applyLoopStationHighlight() { |
| | | if (!this.pixiStaMap) { return; } |
| | | this.pixiStaMap.forEach((sprite) => { |
| | | if (!sprite) { return; } |
| | | if (this.isStationInHoverLoop(sprite)) { |
| | | this.applyHighlightColor(sprite); |
| | | } else if (sprite._loopHighlighted) { |
| | | const baseColor = (typeof sprite._baseColor === 'number') ? sprite._baseColor : 0xb8b8b8; |
| | | this.updateColor(sprite, baseColor); |
| | | sprite._loopHighlighted = false; |
| | | } |
| | | }); |
| | | }, |
| | | clearLoopStationHighlight() { |
| | | if (this.pixiStaMap) { |
| | | this.pixiStaMap.forEach((sprite) => { |
| | | if (!sprite || !sprite._loopHighlighted) { return; } |
| | | const baseColor = (typeof sprite._baseColor === 'number') ? sprite._baseColor : 0xb8b8b8; |
| | | this.updateColor(sprite, baseColor); |
| | | sprite._loopHighlighted = false; |
| | | }); |
| | | } |
| | | this.hoverLoopNo = null; |
| | | this.hoverLoopStationIdSet = new Set(); |
| | | }, |
| | | handleLoopCardEnter(loopItem) { |
| | | if (!loopItem) { return; } |
| | | this.hoverLoopNo = loopItem.loopNo; |
| | | this.hoverLoopStationIdSet = this.buildStationIdSet(loopItem.stationIdList); |
| | | this.applyLoopStationHighlight(); |
| | | }, |
| | | handleLoopCardLeave(loopItem) { |
| | | if (!loopItem) { |
| | | this.clearLoopStationHighlight(); |
| | | return; |
| | | } |
| | | if (this.hoverLoopNo === loopItem.loopNo) { |
| | | this.clearLoopStationHighlight(); |
| | | } |
| | | }, |
| | | isJson(str) { |
| | | try { JSON.parse(str); return true; } catch (e) { return false; } |
| | |
| | | adjustLabelScale() { |
| | | const s = this.pixiApp && this.pixiApp.stage ? Math.abs(this.pixiApp.stage.scale.x || 1) : 1; |
| | | const minPx = 14; |
| | | const vw = this.pixiApp.view.width; |
| | | const vh = this.pixiApp.view.height; |
| | | const viewport = this.getViewportSize(); |
| | | const vw = viewport.width; |
| | | const vh = viewport.height; |
| | | const margin = 50; |
| | | const mirrorSign = this.mapMirrorX ? -1 : 1; |
| | | const inverseRotation = -((this.mapRotation % 360) * Math.PI / 180); |
| | |
| | | const contentW = size.width || 0; |
| | | const contentH = size.height || 0; |
| | | if (contentW <= 0 || contentH <= 0) { return; } |
| | | const vw = this.pixiApp.view.width; |
| | | const vh = this.pixiApp.view.height; |
| | | const viewport = this.getViewportSize(); |
| | | const vw = viewport.width; |
| | | const vh = viewport.height; |
| | | let scale = Math.min(vw / contentW, vh / contentH) * 0.95; |
| | | if (!isFinite(scale) || scale <= 0) { scale = 1; } |
| | | const baseW = this.mapContentSize.width || contentW; |
| | |
| | | } |
| | | } |
| | | }); |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | |
| | | <el-descriptions border direction="vertical"> |
| | | <el-descriptions-item label="模式">{{ item.mode }}</el-descriptions-item> |
| | | <el-descriptions-item label="异常码">{{ item.warnCode }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位1任务号">{{ item.taskNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位2任务号">{{ item.taskNoTwo }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位1任务号"> |
| | | <span v-if="readOnly">{{ item.taskNo }}</span> |
| | | <el-button |
| | | v-else |
| | | type="text" |
| | | size="mini" |
| | | style="padding:0;" |
| | | @click.stop="editTaskNo(item, 1)" |
| | | >{{ item.taskNo }}</el-button> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="工位2任务号"> |
| | | <span v-if="readOnly">{{ item.taskNoTwo }}</span> |
| | | <el-button |
| | | v-else |
| | | type="text" |
| | | size="mini" |
| | | style="padding:0;" |
| | | @click.stop="editTaskNo(item, 2)" |
| | | >{{ item.taskNoTwo }}</el-button> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="设备工位1任务号">{{ item.deviceTaskNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="设备工位2任务号">{{ item.deviceTaskNoTwo }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位1状态">{{ item.status }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位2状态">{{ item.statusTwo }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位1是否有物">{{ item.loading }}</el-descriptions-item> |
| | |
| | | <el-descriptions-item label="工位2货叉定位">{{ item.forkOffsetTwo }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位1任务接收">{{ item.taskReceive }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位2任务接收">{{ item.taskReceiveTwo }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位1下发数据">{{ item.taskSend }}</el-descriptions-item> |
| | | <el-descriptions-item label="工位2下发数据">{{ item.taskSendTwo }}</el-descriptions-item> |
| | | <el-descriptions-item label="列">{{ item.bay }}</el-descriptions-item> |
| | | <el-descriptions-item label="层">{{ item.lev }}</el-descriptions-item> |
| | | <el-descriptions-item label="载货台定位">{{ item.liftPos }}</el-descriptions-item> |
| | |
| | | openControl() { |
| | | this.showControl = !this.showControl; |
| | | }, |
| | | editTaskNo(item, station) { |
| | | let that = this; |
| | | const isStationOne = station === 1; |
| | | const fieldName = isStationOne ? "taskNo" : "taskNoTwo"; |
| | | const stationName = isStationOne ? "工位1" : "工位2"; |
| | | const currentTaskNo = item[fieldName] == null ? "" : String(item[fieldName]); |
| | | that.$prompt("请输入" + stationName + "任务号", "编辑任务号", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | inputValue: currentTaskNo, |
| | | inputPattern: /^\d+$/, |
| | | inputErrorMessage: "任务号必须是非负整数", |
| | | }).then(({ value }) => { |
| | | const taskNo = Number(value); |
| | | $.ajax({ |
| | | url: baseUrl + "/dualcrn/command/updateTaskNo", |
| | | headers: { |
| | | token: localStorage.getItem("token"), |
| | | }, |
| | | contentType: "application/json", |
| | | method: "post", |
| | | data: JSON.stringify({ |
| | | crnNo: item.crnNo, |
| | | station: station, |
| | | taskNo: taskNo, |
| | | }), |
| | | success: (res) => { |
| | | if (res.code == 200) { |
| | | item[fieldName] = taskNo; |
| | | that.$message({ |
| | | message: stationName + "任务号更新成功", |
| | | type: "success", |
| | | }); |
| | | that.getDualCrnStateInfo(); |
| | | } else { |
| | | that.$message({ |
| | | message: res.msg, |
| | | type: "warning", |
| | | }); |
| | | } |
| | | }, |
| | | }); |
| | | }).catch(() => {}); |
| | | }, |
| | | getDualCrnStateInfo() { |
| | | if (this.$root.sendWs) { |
| | | this.$root.sendWs(JSON.stringify({ |
| | |
| | | { field: "inStationList", align: "center", title: "入库站点数据" }, |
| | | { field: "outStationList", align: "center", title: "出库站点数据" }, |
| | | { field: "runBlockReassignLocStationList", align: "center", title: "运行堵塞重新分配库位站点数据" }, |
| | | { field: "isOutOrderList", align: "center", title: "出库排序交互点" }, |
| | | { field: "isLiftTransferList", align: "center", title: "顶升移栽点" }, |
| | | |
| | | { |
| | | fixed: "right", |
| | |
| | | <input class="layui-input" name="runBlockReassignLocStationList" placeholder="请输入运行堵塞重新分配库位站点数据"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-form-item"> |
| | | <label class="layui-form-label">出库排序交互点: </label> |
| | | <div class="layui-input-block"> |
| | | <input class="layui-input" name="isOutOrderList" placeholder="请输入出库排序交互点"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-form-item"> |
| | | <label class="layui-form-label">顶升移栽点: </label> |
| | | <div class="layui-input-block"> |
| | | <input class="layui-input" name="isLiftTransferList" placeholder="请输入电梯中转点"> |
| | | </div> |
| | | </div> |
| | | |
| | | </div> |
| | | </div> |
| | |
| | | </form> |
| | | </script> |
| | | </html> |
| | | |
| | |
| | | > |
| | | <div v-if="drawerLocNoData!=null"> |
| | | <div style="margin: 10px;"> |
| | | <div style="margin-top: 5px;">排:{{drawerLocNoData.row}}</div> |
| | | <div style="margin-top: 5px;">列:{{drawerLocNoData.bay}}</div> |
| | | <div style="margin-top: 5px;">层:{{drawerLocNoData.lev}}</div> |
| | | <!-- <div style="margin-top: 5px;">排:{{drawerLocNoData.row}}</div>--> |
| | | <!-- <div style="margin-top: 5px;">列:{{drawerLocNoData.bay}}</div>--> |
| | | <!-- <div style="margin-top: 5px;">层:{{drawerLocNoData.lev}}</div>--> |
| | | <div style="margin-top: 5px;">库位号:{{drawerLocNoData.locNo}}</div> |
| | | <div style="margin-top: 5px;">库位状态:{{drawerLocNoData.locSts}}</div> |
| | | </div> |
| | |
| | | let pixiStageList = []; |
| | | let pixiStaMap = new Map(); |
| | | let objectsContainer; |
| | | let objectsContainer3; |
| | | let tracksGraphics; |
| | | let mapRoot; |
| | | let mapContentSize = { width: 0, height: 0 }; |
| | | let graphics0; |
| | | let graphicsF; |
| | | let graphics3; |
| | | let graphics4; |
| | | let graphics5; |
| | |
| | | currentLevStaList: [],//当前楼层站点list |
| | | drawerSta: false, |
| | | drawerStaData: null, |
| | | mapRotation: 0, |
| | | mapMirrorX: false, |
| | | mapConfigCodes: { |
| | | rotate: 'map_canvas_rotation', |
| | | mirror: 'map_canvas_mirror_x' |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | |
| | | ws.onclose = this.webSocketClose |
| | | |
| | | |
| | | this.loadMapTransformConfig() |
| | | this.initLev()//初始化楼层信息 |
| | | setTimeout(() => { |
| | | that.getMap(this.currentLev) |
| | |
| | | }, |
| | | changeFloor(lev) { |
| | | this.currentLev = lev |
| | | this.loadMapTransformConfig() |
| | | this.reloadMap = true |
| | | this.getMap(lev) |
| | | }, |
| | |
| | | graphics67 = pixiApp.renderer.generateTexture(getContainer(67)); |
| | | graphicsLock = pixiApp.renderer.generateTexture(getContainer(-999)); |
| | | |
| | | mapRoot = new PIXI.Container(); |
| | | pixiApp.stage.addChild(mapRoot); |
| | | // 创建一个容器来管理大批量的显示对象 |
| | | objectsContainer = new PIXI.Container(); |
| | | pixiApp.stage.addChild(objectsContainer); |
| | | mapRoot.addChild(objectsContainer); |
| | | |
| | | tracksGraphics = new PIXI.Graphics(); |
| | | mapRoot.addChild(tracksGraphics); |
| | | |
| | | // 创建一个容器来管理大批量的显示对象 |
| | | objectsContainer3 = new PIXI.Container(); |
| | | pixiApp.stage.addChild(objectsContainer3); |
| | | mapRoot.addChild(objectsContainer3); |
| | | |
| | | //*******************拖动画布******************* |
| | | let stageOriginalPos; |
| | |
| | | pixiApp.view.addEventListener('wheel', (event) => { |
| | | event.stopPropagation(); |
| | | event.preventDefault(); |
| | | const sx = event.clientX; |
| | | const sy = event.clientY; |
| | | const oldZoom = pixiApp.stage.scale.x; |
| | | const rect = pixiApp.view.getBoundingClientRect(); |
| | | const sx = event.clientX - rect.left; |
| | | const sy = event.clientY - rect.top; |
| | | const oldZoomX = pixiApp.stage.scale.x || 1; |
| | | const oldZoomY = pixiApp.stage.scale.y || 1; |
| | | const oldZoomAbs = Math.abs(oldZoomX) || 1; |
| | | const delta = event.deltaY; |
| | | let newZoom = oldZoom * 0.999 ** delta; |
| | | const worldX = (sx - pixiApp.stage.position.x) / oldZoom; |
| | | const worldY = (sy - pixiApp.stage.position.y) / oldZoom; |
| | | const newPosX = sx - worldX * newZoom; |
| | | const newPosY = sy - worldY * newZoom; |
| | | pixiApp.stage.setTransform(newPosX, newPosY, newZoom, newZoom, 0, 0, 0, 0, 0); |
| | | let newZoomAbs = oldZoomAbs * 0.999 ** delta; |
| | | const mirrorX = this.mapMirrorX ? -1 : 1; |
| | | const newZoomX = mirrorX * newZoomAbs; |
| | | const newZoomY = newZoomAbs; |
| | | const worldX = (sx - pixiApp.stage.position.x) / oldZoomX; |
| | | const worldY = (sy - pixiApp.stage.position.y) / oldZoomY; |
| | | const newPosX = sx - worldX * newZoomX; |
| | | const newPosY = sy - worldY * newZoomY; |
| | | pixiApp.stage.setTransform(newPosX, newPosY, newZoomX, newZoomY, 0, 0, 0, 0, 0); |
| | | |
| | | }); |
| | | //*******************缩放画布******************* |
| | |
| | | pixiStageList = [map.length]//初始化列表 |
| | | pixiStaMap = new Map();//重置 |
| | | objectsContainer.removeChildren() |
| | | if (tracksGraphics) { tracksGraphics.clear(); } |
| | | map.forEach((item,index) => { |
| | | pixiStageList[index] = [item.length] |
| | | for (let idx = 0; idx < item.length; idx++) { |
| | |
| | | }); |
| | | |
| | | const b1 = objectsContainer.getLocalBounds(); |
| | | const minX = Math.min(b1.x); |
| | | const minY = Math.min(b1.y); |
| | | const maxX = Math.max(b1.x + b1.width); |
| | | const maxY = Math.max(b1.y + b1.height); |
| | | const minX = b1.x; |
| | | const minY = b1.y; |
| | | const maxX = b1.x + b1.width; |
| | | const maxY = b1.y + b1.height; |
| | | const contentW = Math.max(0, maxX - minX); |
| | | const contentH = Math.max(0, maxY - minY); |
| | | const vw = pixiApp.view.width; |
| | | const vh = pixiApp.view.height; |
| | | let scale = Math.min(vw / contentW, vh / contentH) * 0.95; |
| | | if (!isFinite(scale) || scale <= 0) { scale = 1; } |
| | | const posX = (vw - contentW * scale) / 2 - minX * scale; |
| | | const posY = (vh - contentH * scale) / 2 - minY * scale; |
| | | pixiApp.stage.setTransform(posX, posY, scale, scale, 0, 0, 0, 0, 0); |
| | | mapContentSize = { width: contentW, height: contentH }; |
| | | this.drawTracks(map); |
| | | this.applyMapTransform(true); |
| | | } |
| | | this.map = map; |
| | | }, |
| | | isTrackCell(cell) { |
| | | if (!cell) { return false; } |
| | | const type = cell.type ? String(cell.type).toLowerCase() : ''; |
| | | if (type === 'track' || type === 'crn' || type === 'dualcrn' || type === 'rgv') { return true; } |
| | | if (cell.trackSiteNo != null) { return true; } |
| | | const v = parseInt(cell.value, 10); |
| | | if (v === 3 || v === 9) { return true; } |
| | | if (cell.value != null) { |
| | | try { |
| | | const obj = (typeof cell.value === 'string') ? JSON.parse(cell.value) : cell.value; |
| | | if (obj && (obj.trackSiteNo != null || (obj.deviceNo != null && (type === 'crn' || type === 'dualcrn' || type === 'rgv')))) { |
| | | return true; |
| | | } |
| | | } catch (e) {} |
| | | } |
| | | return false; |
| | | }, |
| | | drawTracks(map) { |
| | | if (!tracksGraphics || !Array.isArray(map)) { return; } |
| | | tracksGraphics.clear(); |
| | | const railColor = 0x6c727a; |
| | | const railWidth = Math.max(1, Math.round(Math.min(width, height) * 0.08)); |
| | | tracksGraphics.lineStyle(railWidth, railColor, 1); |
| | | |
| | | for (let r = 0; r < map.length; r++) { |
| | | const row = map[r]; |
| | | if (!Array.isArray(row)) { continue; } |
| | | for (let c = 0; c < row.length; c++) { |
| | | const cell = row[c]; |
| | | if (!this.isTrackCell(cell)) { continue; } |
| | | const cx = c * width + width / 2; |
| | | const cy = r * height + height / 2; |
| | | const up = (r - 1 >= 0 && Array.isArray(map[r - 1])) ? map[r - 1][c] : null; |
| | | const right = (c + 1 < row.length) ? row[c + 1] : null; |
| | | const down = (r + 1 < map.length && Array.isArray(map[r + 1])) ? map[r + 1][c] : null; |
| | | const left = (c - 1 >= 0) ? row[c - 1] : null; |
| | | const hasN = this.isTrackCell(up); |
| | | const hasE = this.isTrackCell(right); |
| | | const hasS = this.isTrackCell(down); |
| | | const hasW = this.isTrackCell(left); |
| | | const seg = Math.min(width, height) * 0.5; |
| | | let drew = false; |
| | | if (hasN) { tracksGraphics.moveTo(cx, cy); tracksGraphics.lineTo(cx, cy - seg); drew = true; } |
| | | if (hasE) { tracksGraphics.moveTo(cx, cy); tracksGraphics.lineTo(cx + seg, cy); drew = true; } |
| | | if (hasS) { tracksGraphics.moveTo(cx, cy); tracksGraphics.lineTo(cx, cy + seg); drew = true; } |
| | | if (hasW) { tracksGraphics.moveTo(cx, cy); tracksGraphics.lineTo(cx - seg, cy); drew = true; } |
| | | if (!drew) { |
| | | tracksGraphics.moveTo(cx - seg * 0.4, cy); |
| | | tracksGraphics.lineTo(cx + seg * 0.4, cy); |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | parseRotation(value) { |
| | | const num = parseInt(value, 10); |
| | | if (!isFinite(num)) { return 0; } |
| | | const rot = ((num % 360) + 360) % 360; |
| | | return (rot === 90 || rot === 180 || rot === 270) ? rot : 0; |
| | | }, |
| | | parseMirror(value) { |
| | | if (value === true || value === false) { return value; } |
| | | if (value == null) { return false; } |
| | | const str = String(value).toLowerCase(); |
| | | return str === '1' || str === 'true' || str === 'y'; |
| | | }, |
| | | loadMapTransformConfig() { |
| | | if (!window.$ || typeof baseUrl === 'undefined') { return; } |
| | | $.ajax({ |
| | | url: baseUrl + "/config/listAll/auth", |
| | | headers: { 'token': localStorage.getItem('token') }, |
| | | dataType: 'json', |
| | | method: 'GET', |
| | | success: (res) => { |
| | | if (!res || res.code !== 200 || !Array.isArray(res.data)) { |
| | | if (res && res.code === 403) { parent.location.href = baseUrl + "/login"; } |
| | | return; |
| | | } |
| | | const byCode = {}; |
| | | res.data.forEach((item) => { |
| | | if (item && item.code) { byCode[item.code] = item; } |
| | | }); |
| | | const rotateCfg = byCode[this.mapConfigCodes.rotate]; |
| | | const mirrorCfg = byCode[this.mapConfigCodes.mirror]; |
| | | if (rotateCfg && rotateCfg.value != null) { |
| | | this.mapRotation = this.parseRotation(rotateCfg.value); |
| | | } |
| | | if (mirrorCfg && mirrorCfg.value != null) { |
| | | this.mapMirrorX = this.parseMirror(mirrorCfg.value); |
| | | } |
| | | if (mapContentSize && mapContentSize.width > 0 && mapContentSize.height > 0) { |
| | | this.applyMapTransform(true); |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | getViewportSize() { |
| | | if (!pixiApp || !pixiApp.renderer) { return { width: 0, height: 0 }; } |
| | | const screen = pixiApp.renderer.screen; |
| | | if (screen && screen.width > 0 && screen.height > 0) { |
| | | return { width: screen.width, height: screen.height }; |
| | | } |
| | | const rect = pixiApp.view ? pixiApp.view.getBoundingClientRect() : null; |
| | | return { width: rect ? rect.width : 0, height: rect ? rect.height : 0 }; |
| | | }, |
| | | getTransformedContentSize() { |
| | | const size = mapContentSize || { width: 0, height: 0 }; |
| | | const w = size.width || 0; |
| | | const h = size.height || 0; |
| | | const rot = ((this.mapRotation % 360) + 360) % 360; |
| | | const swap = rot === 90 || rot === 270; |
| | | return { width: swap ? h : w, height: swap ? w : h }; |
| | | }, |
| | | fitStageToContent() { |
| | | if (!pixiApp || !mapContentSize) { return; } |
| | | const size = this.getTransformedContentSize(); |
| | | const contentW = size.width || 0; |
| | | const contentH = size.height || 0; |
| | | if (contentW <= 0 || contentH <= 0) { return; } |
| | | const viewport = this.getViewportSize(); |
| | | const vw = viewport.width; |
| | | const vh = viewport.height; |
| | | let scale = Math.min(vw / contentW, vh / contentH) * 0.95; |
| | | if (!isFinite(scale) || scale <= 0) { scale = 1; } |
| | | const baseW = mapContentSize.width || contentW; |
| | | const baseH = mapContentSize.height || contentH; |
| | | const mirrorX = this.mapMirrorX ? -1 : 1; |
| | | const scaleX = scale * mirrorX; |
| | | const scaleY = scale; |
| | | const posX = (vw / 2) - (baseW / 2) * scaleX; |
| | | const posY = (vh / 2) - (baseH / 2) * scaleY; |
| | | pixiApp.stage.setTransform(posX, posY, scaleX, scaleY, 0, 0, 0, 0, 0); |
| | | }, |
| | | applyMapTransform(fitToView) { |
| | | if (!mapRoot || !mapContentSize) { return; } |
| | | const contentW = mapContentSize.width || 0; |
| | | const contentH = mapContentSize.height || 0; |
| | | if (contentW <= 0 || contentH <= 0) { return; } |
| | | mapRoot.pivot.set(contentW / 2, contentH / 2); |
| | | mapRoot.position.set(contentW / 2, contentH / 2); |
| | | mapRoot.rotation = (this.mapRotation % 360) * Math.PI / 180; |
| | | mapRoot.scale.set(1, 1); |
| | | if (fitToView) { this.fitStageToContent(); } |
| | | }, |
| | | rightEvent(x, y, e) { |
| | | this.drawerLocNo = true |
| | |
| | | sprite = new PIXI.Sprite(graphics0); |
| | | } |
| | | sprite.position.set(x, y); |
| | | const type = item && item.type ? String(item.type).toLowerCase() : ''; |
| | | const numVal = parseInt(value, 10); |
| | | const isTrackCell = numVal === 3 || numVal === 9 || type === 'track' || type === 'crn' || type === 'dualcrn' || type === 'rgv'; |
| | | if (!isTrackCell) { |
| | | sprite.interactive = true; // 必须要设置才能接收事件 |
| | | sprite.buttonMode = true; // 让光标在hover时变为手型指针 |
| | | |
| | | sprite.on('pointerdown', (e) => { |
| | | pointerDownEvent(e) |
| | | }) |
| | | } |
| | | |
| | | return sprite; |
| | | } |
| | |
| | | $('#btn-server-info').click(function() { |
| | | $.ajax({ |
| | | url: baseUrl + "/license/getServerInfos", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'GET', |
| | | success: function (res) { |
| | | var pretty = ''; |
| | |
| | | layer.close(index); |
| | | $.ajax({ |
| | | url: baseUrl + "/license/activate", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'POST', |
| | | success: function (res) { |
| | | if (res.code === 200){ |
| | |
| | | $('#btn-project-name').click(function() { |
| | | $.ajax({ |
| | | url: baseUrl + "/license/getProjectName", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'GET', |
| | | success: function (res) { |
| | | if (res.code === 200){ |
| | |
| | | <link rel="stylesheet" href="../../static/css/animate.min.css"> |
| | | <link rel="stylesheet" href="../../static/vue/element/element.css"> |
| | | <link rel="stylesheet" href="../../static/css/watch/console_vue.css"> |
| | | <style> |
| | | html, body, #app { |
| | | width: 100%; |
| | | height: 100%; |
| | | margin: 0; |
| | | overflow: hidden; |
| | | } |
| | | </style> |
| | | <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../../static/layui/layui.js"></script> |
| | | <script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script> |