From eeeffbb708dfd5baaa05327ec8128640bc2cc58b Mon Sep 17 00:00:00 2001 From: zhangzq Date: Mon, 8 Jan 2024 17:39:11 +0800 Subject: [PATCH] =?UTF-8?q?opt:=E5=8F=91=E8=B4=A7=E5=8C=BA=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E6=8E=A5=E5=8F=A3=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/nl/common/utils/RedissonUtils.java | 44 ++ .../websocket/SendRegionWebSocketServer.java | 110 +++++ .../st/service/StructattrService.java | 1 + .../service/impl/StructattrServiceImpl.java | 117 +++++- .../nl/wms/basedata/st/wql/QST_STRUCTATTR.wql | 26 +- .../src/views/wms/sendout/outone/index.vue | 47 ++- .../src/views/wms/sendout/outone/index3.vue | 389 ++++++++++++++++++ 7 files changed, 711 insertions(+), 23 deletions(-) create mode 100644 lms/nladmin-system/src/main/java/org/nl/common/utils/RedissonUtils.java create mode 100644 lms/nladmin-system/src/main/java/org/nl/modules/mnt/websocket/SendRegionWebSocketServer.java create mode 100644 lms/nladmin-ui/src/views/wms/sendout/outone/index3.vue diff --git a/lms/nladmin-system/src/main/java/org/nl/common/utils/RedissonUtils.java b/lms/nladmin-system/src/main/java/org/nl/common/utils/RedissonUtils.java new file mode 100644 index 000000000..01ecd5799 --- /dev/null +++ b/lms/nladmin-system/src/main/java/org/nl/common/utils/RedissonUtils.java @@ -0,0 +1,44 @@ +package org.nl.common.utils; + +import lombok.SneakyThrows; +import org.nl.modules.wql.util.SpringContextHolder; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; + +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.Supplier; + +/* + * @author ZZQ + * @Date 2023/3/27 10:30 + */ +public class RedissonUtils { + /** + * + * @param process 业务代码 + * @param key + * @param seconds 尝试获取锁的等待时间,允许为空 + */ + @SneakyThrows + public static void lock(Function process, String key, Object param){ + RedissonClient redissonClient = SpringContextHolder.getBean(RedissonClient.class); + RLock lock = redissonClient.getLock(key); + boolean isLock; + isLock = lock.tryLock(); + try { + if (isLock){ + process.apply(param); + } else { + return; + } + }catch (Exception ex){ + ex.printStackTrace(); + throw ex; + }finally { + if (isLock && lock.isHeldByCurrentThread()){ + lock.unlock(); + } + } + } +} diff --git a/lms/nladmin-system/src/main/java/org/nl/modules/mnt/websocket/SendRegionWebSocketServer.java b/lms/nladmin-system/src/main/java/org/nl/modules/mnt/websocket/SendRegionWebSocketServer.java new file mode 100644 index 000000000..1c916070b --- /dev/null +++ b/lms/nladmin-system/src/main/java/org/nl/modules/mnt/websocket/SendRegionWebSocketServer.java @@ -0,0 +1,110 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.nl.modules.mnt.websocket; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.nl.common.utils.RedissonUtils; +import org.nl.modules.mnt.service.AutoRiKuService; +import org.nl.modules.wql.util.SpringContextHolder; +import org.nl.wms.basedata.st.service.StructattrService; +import org.springframework.stereotype.Component; + +import javax.websocket.*; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArraySet; + +/* + * @author ZZQ + * @Date 2024/1/8 17:03 + */ +@ServerEndpoint("/webSocket/sendRegion/{sid}") +@Slf4j +@Component +public class SendRegionWebSocketServer { + + /** + * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 + */ + private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet(); + /** + * 与某个客户端的连接会话,需要通过它来给客户端发送数据 + */ + private Session session; + /** + * 接收sid + */ + private String sid = ""; + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(Session session, @PathParam("sid") String sid) { + this.session = session; + //如果存在就先删除一个,防止重复推送消息 + for (SendRegionWebSocketServer webSocket : webSocketSet) { + if (webSocket.sid.equals(sid)) { + webSocketSet.remove(webSocket); + } + } + webSocketSet.add(this); + this.sid = sid; + } + + /** + * 连接关闭调用的方法 + */ + @OnClose + public void onClose() { + webSocketSet.remove(this); + } + + /** + * 收到客户端消息后调用的方法 + * + * @param message 客户端发送过来的消息 + */ + @OnMessage + public void onMessage(String message, Session session) { + System.out.println(webSocketSet.size()+"_接收到消息_"+session.getId()); + if (StringUtils.isNotEmpty(message)){ + RedissonUtils.lock(key -> { + StructattrService service = SpringContextHolder.getBean(StructattrService.class); + Map record = service.getStructByCodesFsAnNum(JSONArray.parseArray(message)); + try { + Thread.sleep(2000); + for (SendRegionWebSocketServer consumer : webSocketSet) { + consumer.session.getBasicRemote().sendText(JSON.toJSONString(record)); + } + }catch (Exception ex){ + ex.printStackTrace(); + } + return ""; + }, message, message); + } + } + + @OnError + public void onError(Session session, Throwable error) { + log.error("发生错误"); + webSocketSet.remove(session); + error.printStackTrace(); + } +} diff --git a/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/service/StructattrService.java b/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/service/StructattrService.java index 49eea2349..aa54af38d 100644 --- a/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/service/StructattrService.java +++ b/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/service/StructattrService.java @@ -90,6 +90,7 @@ public interface StructattrService { * @return */ JSONArray getStructByCodesFs(JSONArray json); + Map getStructByCodesFsAnNum(JSONArray json); /** * 解锁点位 diff --git a/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/service/impl/StructattrServiceImpl.java b/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/service/impl/StructattrServiceImpl.java index c8f014979..c31817edd 100644 --- a/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/service/impl/StructattrServiceImpl.java +++ b/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/service/impl/StructattrServiceImpl.java @@ -28,9 +28,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; /** * @author zhouz @@ -319,6 +322,33 @@ public class StructattrServiceImpl implements StructattrService { public JSONArray getStructByCodesFs(JSONArray jsonArray) { WQLObject attrTab = WQLObject.getWQLObject("sch_base_point"); JSONArray arr = new JSONArray(); + List list = new ArrayList<>(); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject js = jsonArray.getJSONObject(i); + String struct_id = js.getString("struct_id"); + if (ObjectUtil.isEmpty(struct_id)) { + continue; + } + list.add(struct_id); + } + String sql = "('" + list.stream().collect(Collectors.joining("','")) + "')"; + JSONArray array = WQL + .getWO("QST_STRUCTATTR") + .addParamMap(MapOf.of("struct_ids", sql, "flag", "3")) + .process() + .getResultJSONArray(0); + + Map pointMap = array.stream().collect(HashMap::new, (BiConsumer, Object>) (m, o) -> { + JSONObject item = (JSONObject) o; + m.put(item.getString("point_id"), item); + }, HashMap::putAll); + + JSONArray attrs = attrTab.query("point_id in " + sql).getResultJSONArray(0); + Map attrMap = attrs.stream().collect(HashMap::new, (BiConsumer, Object>) (m, o) -> { + JSONObject item = (JSONObject) o; + m.put(item.getString("point_id"), item); + }, HashMap::putAll); + for (int i = 0; i < jsonArray.size(); i++) { JSONObject obj = new JSONObject(); JSONObject js = jsonArray.getJSONObject(i); @@ -327,18 +357,10 @@ public class StructattrServiceImpl implements StructattrService { if (ObjectUtil.isEmpty(struct_id)) { continue; } - int struct_status = 2; // 获取信息 1蓝色空载具 2黄色木箱 3绿色空位 4灰色禁用 - // 查找详细信息, 这里可能是有多条数据 - JSONArray array = WQL - .getWO("QST_STRUCTATTR") - .addParamMap(MapOf.of("struct_id", struct_id, "flag", "2")) - .process() - .getResultJSONArray(0); - // 计算子卷净重 - JSONObject json = array.getJSONObject(0); + JSONObject json = pointMap.get(struct_id); if (ObjectUtil.isNotEmpty(json.getString("net_weight"))) { BigDecimal container_weight = array.stream().map(row -> ((JSONObject) row).getBigDecimal("net_weight")).reduce(BigDecimal.ZERO, BigDecimal::add); @@ -348,11 +370,8 @@ public class StructattrServiceImpl implements StructattrService { } } - // 获取仓位表中的信息 - JSONObject strInfo = attrTab - .query("point_id = '" + struct_id + "'") - .uniqueResult(0); - if (ObjectUtil.isEmpty(strInfo.getString("vehicle_code"))) { + JSONObject strInfo = attrMap.get(struct_id); + if (strInfo !=null && ObjectUtil.isEmpty(strInfo.getString("vehicle_code"))) { // 空位 struct_status = 3; } @@ -368,6 +387,76 @@ public class StructattrServiceImpl implements StructattrService { return arr; } + @Override + public Map getStructByCodesFsAnNum(JSONArray jsonArray) { + WQLObject attrTab = WQLObject.getWQLObject("sch_base_point"); + JSONArray arr = new JSONArray(); + List list = new ArrayList<>(); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject js = jsonArray.getJSONObject(i); + String struct_id = js.getString("struct_id"); + if (ObjectUtil.isEmpty(struct_id)) { + continue; + } + list.add(struct_id); + } + String sql = "('" + list.stream().collect(Collectors.joining("','")) + "')"; + JSONArray array = WQL + .getWO("QST_STRUCTATTR") + .addParamMap(MapOf.of("struct_ids", sql, "flag", "3")) + .process() + .getResultJSONArray(0); + + Map pointMap = array.stream().collect(HashMap::new, (BiConsumer, Object>) (m, o) -> { + JSONObject item = (JSONObject) o; + m.put(item.getString("point_id"), item); + }, HashMap::putAll); + + JSONArray attrs = attrTab.query("point_id in " + sql).getResultJSONArray(0); + Map attrMap = attrs.stream().collect(HashMap::new, (BiConsumer, Object>) (m, o) -> { + JSONObject item = (JSONObject) o; + m.put(item.getString("point_id"), item); + }, HashMap::putAll); + int unMoney = 0; + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject obj = new JSONObject(); + JSONObject js = jsonArray.getJSONObject(i); + String struct_id = js.getString("struct_id"); + + if (ObjectUtil.isEmpty(struct_id)) { + continue; + } + int struct_status = 2; + // 获取信息 1蓝色空载具 2黄色木箱 3绿色空位 4灰色禁用 + // 计算子卷净重 + JSONObject json = pointMap.get(struct_id); + if (ObjectUtil.isNotEmpty(json.getString("net_weight"))) { + BigDecimal container_weight = array.stream().map(row -> ((JSONObject) row).getBigDecimal("net_weight")).reduce(BigDecimal.ZERO, BigDecimal::add); + + for (int j = 0; j < array.size(); j++) { + JSONObject jsonObject = array.getJSONObject(j); + jsonObject.put("container_weight", container_weight); + } + } + + JSONObject strInfo = attrMap.get(struct_id); + if (strInfo !=null && ObjectUtil.isEmpty(strInfo.getString("vehicle_code"))) { + // 空位 + unMoney++; + struct_status = 3; + } + + // 剩余层数货位信息没统计,统计可以用obj.put(key, value)返给前端,前端在initStatus()就可以遍历去获取,给节点赋值 + obj.put("data", array); + obj.put("struct_status", struct_status); + obj.put("struct_id", strInfo.getString("point_id")); + obj.put("struct_code", strInfo.getString("point_code")); + obj.put("id", js.getString("id")); // 设备不存在就只保留id,方便前端查看 + arr.add(obj); + } + return MapOf.of("data",arr,"haveMoney",arr.size()-unMoney,"unMoney",unMoney); + } + @Override @Transactional(rollbackFor = Exception.class) public JSONObject unLockPoint(JSONObject whereJson) { diff --git a/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/wql/QST_STRUCTATTR.wql b/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/wql/QST_STRUCTATTR.wql index e75839db6..ffb79ab1a 100644 --- a/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/wql/QST_STRUCTATTR.wql +++ b/lms/nladmin-system/src/main/java/org/nl/wms/basedata/st/wql/QST_STRUCTATTR.wql @@ -14,7 +14,8 @@ ## 表字段对应输入参数 ################################################# 输入.flag TYPEAS s_string - 输入.struct_id TYPEAS s_string + 输入.struct_id TYPEAS s_string + 输入.struct_ids TYPEAS f_string [临时表] --这边列出来的临时表就会在运行期动态创建 @@ -83,3 +84,26 @@ ENDSELECT ENDQUERY ENDIF + + IF 输入.flag = "3" + QUERY + SELECT + attr.*, + sub.package_box_sn, + sub.quanlity_in_box, + sub.box_weight, + sub.sale_order_name, + sub.container_name, + sub.product_description, + sub.net_weight + FROM + sch_base_point attr + LEFT JOIN pdm_bi_subpackagerelation sub ON sub.package_box_sn = attr.vehicle_code + WHERE + 1 = 1 + OPTION 输入.struct_ids <> "" + attr.point_id in 输入.struct_ids + ENDOPTION + ENDSELECT + ENDQUERY + ENDIF diff --git a/lms/nladmin-ui/src/views/wms/sendout/outone/index.vue b/lms/nladmin-ui/src/views/wms/sendout/outone/index.vue index a5a2ee82d..d4d7d7af4 100644 --- a/lms/nladmin-ui/src/views/wms/sendout/outone/index.vue +++ b/lms/nladmin-ui/src/views/wms/sendout/outone/index.vue @@ -95,6 +95,7 @@ import crudStage from '@/api/logicflow/stage' import '@logicflow/core/dist/style/index.css' import '@logicflow/extension/lib/style/index.css' +import uuidv1 from 'uuid/v1' import { LogicFlow } from '@logicflow/core' import { registerCustomElement } from '@/views/system/logicflow/editor/components/node' @@ -112,6 +113,7 @@ export default { tops: '20vh', stage_code: '', stageSelectList: [], + resion: {}, arr: [], // 显示数组 dialogFormVisible: false, dialogFormVisible1: false, @@ -139,6 +141,21 @@ export default { layerNum: '1' } }, + created() { + if (typeof (WebSocket) === 'undefined') { + this.$notify({ + title: '提示', + message: '当前浏览器无法接收实时报警信息,请使用谷歌浏览器!', + type: 'warning', + duration: 0 + }) + } + // const wsUri = (process.env.VUE_APP_WS_API === '/' ? '/' : (process.env.VUE_APP_WS_API + '/')) + 'messageInfo' + const wsUri = window.g.prod.VUE_APP_BASE_API.replace('http', 'ws') + '/webSocket/sendRegion/' + uuidv1() + this.websock = new WebSocket(wsUri) + this.websock.onerror = this.webSocketOnError + this.websock.onmessage = this.webSocketOnMessage + }, mounted() { this.init() this.queryNum() @@ -203,7 +220,6 @@ export default { }) // 右键单击事件 lf.on('node:contextmenu', (data, e) => { - debugger let item = '' item = this.allStructMsg.find((structMsg) => structMsg.id === data.data.id) @@ -220,6 +236,24 @@ export default { lf.render(data) this.initStageData() }, + webSocketOnMessage(e) { + const records = JSON.parse(e.data) + this.allStructMsg = records.data + this.numData.haveMoney = records.haveMoney + this.numData.unMoney = records.unMoney + for (var item of records.data) { // 循环设置属性 + if (item.struct_status != undefined) { + lf.setProperties(item.id, { + struct_status: item.struct_status + }) + } + } + const { nodes } = lf.getSelectElements() // 获取选中的节点 + if (nodes.length === 1) { // 因为是定时器,没有选中则不用实时更新显示数据 + this.moveShow(nodes[0]) // 监控模式下不可能托选,因此就只有一个数据 + } + }, + initStageData() { // 获取舞台编码 crudStage.getNewStageDataByCode(this.stageParam).then(res => { @@ -230,15 +264,12 @@ export default { // todo: 定时器 this.timer = setInterval(() => { // 定时刷新设备的状态信息 console.log('定时器启动') - this.initStatus() - this.queryNum() - }, 10000) + this.websock.send(JSON.stringify(this.resion)) + }, 6000) }, initStatus() { // 初始化数据 - let resion = {} - debugger - resion = lf.getGraphData().nodes.map(item => ({ id: item.id, struct_id: item.properties.struct_id })) - getStructByCodesFs(resion).then(res => { + this.resion = lf.getGraphData().nodes.map(item => ({ id: item.id, struct_id: item.properties.struct_id })) + getStructByCodesFs(this.resion).then(res => { this.allStructMsg = res // 实时设置状态信息 for (var item of res) { // 循环设置属性 diff --git a/lms/nladmin-ui/src/views/wms/sendout/outone/index3.vue b/lms/nladmin-ui/src/views/wms/sendout/outone/index3.vue new file mode 100644 index 000000000..a5a2ee82d --- /dev/null +++ b/lms/nladmin-ui/src/views/wms/sendout/outone/index3.vue @@ -0,0 +1,389 @@ + + + + + +