From 6489ad259cf7e57c2154240d4abdb695ff784955 Mon Sep 17 00:00:00 2001 From: gengby <858962040@qq.com> Date: Tue, 19 Mar 2024 15:46:02 +0800 Subject: [PATCH] =?UTF-8?q?rev=EF=BC=9Amqtt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/nl/acs/device.xls | Bin 87552 -> 87552 bytes .../org/nl/acs/device/service/dto/OpcDto.java | 5 + .../device/service/impl/OpcServiceImpl.java | 8 +- .../nl/acs/device_driver/DeviceDriver.java | 3 + .../org/nl/acs/opc/DeviceExecuteAutoRun.java | 2 +- .../nl/acs/opc/DeviceOpcProtocolRunable.java | 2 +- .../task/service/impl/TaskServiceImpl.java | 73 ++-- .../nl/acs/udw/mqttUdw/ItemsDataAccessor.java | 17 + .../acs/udw/mqttUdw/ItemsProcessService.java | 37 ++ .../factory/ItemDataAccessorFactory.java | 18 + .../factory/ItemsProcessServiceFactory.java | 17 + .../nl/acs/udw/mqttUdw/service/ItemData.java | 28 ++ .../nl/acs/udw/mqttUdw/service/ItemUnit.java | 25 ++ .../service/ItemsDataAccessorImpl.java | 39 ++ .../udw/mqttUdw/service/ItemsProcessImpl.java | 125 ++++++ .../java/org/nl/config/mqtt2/ItemUtil.java | 48 ++ .../java/org/nl/config/mqtt2/MQServer.java | 78 ---- .../java/org/nl/config/mqtt2/MqttServer.java | 412 ++++++++++++++++++ .../java/org/nl/config/mqtt2/PublishDemo.java | 28 -- .../mqtt2/callback/PublishCallback.java | 29 -- .../mqtt2/callback/SubsribeCallback.java | 31 -- .../nl/config/mqtt2/config/MqttConfig.java | 28 +- .../nl/config/mqtt2/msg/DeviceItemData.java | 18 + .../nl/config/mqtt2/msg/MsgPoolManager.java | 78 ---- .../org/nl/config/mqtt2/msg/MsgWorker.java | 27 -- .../quartz/task/AutoCreateAgvOneInst.java | 10 +- .../modules/quartz/task/AutoCreateHrInst.java | 60 +++ .../modules/quartz/task/AutoCreateInst.java | 7 +- .../quartz/task/AutoCreateNuoBaoInst.java | 130 ++++++ 29 files changed, 1052 insertions(+), 331 deletions(-) create mode 100644 wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/ItemsDataAccessor.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/ItemsProcessService.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/factory/ItemDataAccessorFactory.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/factory/ItemsProcessServiceFactory.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemData.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemUnit.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemsDataAccessorImpl.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemsProcessImpl.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/ItemUtil.java delete mode 100644 wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/MQServer.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/MqttServer.java delete mode 100644 wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/PublishDemo.java delete mode 100644 wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/callback/PublishCallback.java delete mode 100644 wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/callback/SubsribeCallback.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/DeviceItemData.java delete mode 100644 wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/MsgPoolManager.java delete mode 100644 wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/MsgWorker.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateHrInst.java create mode 100644 wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateNuoBaoInst.java diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/device.xls b/wcs/nladmin-system/src/main/java/org/nl/acs/device.xls index 1dc1fdffe382f0c8c4294706798a66f41a02473b..69c0ee5b1e007994a3cfc9d7d63e4b38b8bb2124 100644 GIT binary patch literal 87552 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 queryAll(Map whereJson) { WQLObject wo = WQLObject.getWQLObject("acs_opc"); JSONArray arr = wo.query().getResultJSONArray(0); + if (ObjectUtil.isEmpty(arr)) { + return new ArrayList<>(); + } List list = arr.toJavaList(OpcDto.class); return list; } @@ -119,7 +123,7 @@ public class OpcServiceImpl implements OpcService { dto.setCreate_time(now); WQLObject wo = WQLObject.getWQLObject("acs_opc"); - JSONObject json = (JSONObject) JSONObject.toJSON(dto); + JSONObject json = (JSONObject) JSONObject.toJSON(dto); wo.insert(json); } @@ -136,7 +140,7 @@ public class OpcServiceImpl implements OpcService { dto.setUpdate_by(currentUsername); WQLObject wo = WQLObject.getWQLObject("acs_opc"); - JSONObject json = (JSONObject) JSONObject.toJSON(dto); + JSONObject json = (JSONObject) JSONObject.toJSON(dto); wo.update(json); } diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/DeviceDriver.java b/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/DeviceDriver.java index 3f702c59..ccf631e3 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/DeviceDriver.java +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/DeviceDriver.java @@ -5,6 +5,9 @@ import org.nl.acs.opc.Device; import java.util.List; public interface DeviceDriver { + + + default String getDeviceCode() { return this.getDevice().getDevice_code(); } diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/opc/DeviceExecuteAutoRun.java b/wcs/nladmin-system/src/main/java/org/nl/acs/opc/DeviceExecuteAutoRun.java index bfb40096..7e7295f0 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/acs/opc/DeviceExecuteAutoRun.java +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/opc/DeviceExecuteAutoRun.java @@ -77,7 +77,7 @@ public class DeviceExecuteAutoRun extends AbstractAutoRunnable { for (int i = 0; !OpcStartTag.is_run; ++i) { log.info("设备执行线程等待opc同步线程..."); Thread.sleep(1000L); - if (i > 60) { + if (i > 20) { log.info("设备执行线程放弃等待opc同步线程..."); break; } diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/opc/DeviceOpcProtocolRunable.java b/wcs/nladmin-system/src/main/java/org/nl/acs/opc/DeviceOpcProtocolRunable.java index f5476f9b..49fd24e3 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/acs/opc/DeviceOpcProtocolRunable.java +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/opc/DeviceOpcProtocolRunable.java @@ -79,7 +79,7 @@ public class DeviceOpcProtocolRunable implements Runnable, DataCallback, ServerC if (OpcConfig.opc_item_read_using_callback) { this.runNew(); } else { - this.runOld(); + //this.runOld(); } } diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/task/service/impl/TaskServiceImpl.java b/wcs/nladmin-system/src/main/java/org/nl/acs/task/service/impl/TaskServiceImpl.java index 06e8b1c7..4ac7504e 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/acs/task/service/impl/TaskServiceImpl.java +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/task/service/impl/TaskServiceImpl.java @@ -590,42 +590,43 @@ public class TaskServiceImpl implements TaskService, ApplicationAutoInitial { @Override public void createInst(String ids) throws Exception { - TaskDto acsTask = this.findById(ids); - if (acsTask == null) throw new BadRequestException("被删除或无权限,操作失败!"); - List tasksByLinkNum = this.findByLinkNumFromCache(acsTask.getLink_num()); - String link_no = CodeUtil.getNewCode("LINK_NO"); - if (tasksByLinkNum.size() == 1) { - TaskDto taskDto = tasksByLinkNum.get(0); - Instruction instruction = instructionService.findByTaskCodeFromCache(taskDto.getTask_code()); - if (instruction != null) { - throw new BadRequestException("单任务有指令未完成!指令号:" + instruction.getInstruction_code()); - } - Instruction dto = instructionService.createInstDtoByTask(taskDto, link_no); - instructionService.create(dto); - taskDto.setTask_status(StatusEnum.TASK_RUNNING.getCode()); - this.update(taskDto); - } - if (tasksByLinkNum.size() == 2) { - TaskDto taskDto1 = tasksByLinkNum.get(0); - Instruction inst1 = instructionService.findByTaskCodeFromCache(taskDto1.getTask_code()); - if (inst1 != null) { - throw new BadRequestException("双任务存有指令未完成!指令号:" + inst1.getInstruction_code()); - } - TaskDto taskDto2 = tasksByLinkNum.get(1); - Instruction inst2 = instructionService.findByTaskCodeFromCache(taskDto2.getTask_code()); - if (inst2 != null) { - throw new BadRequestException("双任务有指令未完成!指令号:" + inst2.getInstruction_code()); - } - Instruction dto1 = instructionService.createInstDtoByTask(taskDto1, link_no); - Instruction dto2 = instructionService.createInstDtoByTask(taskDto2, link_no); - instructionService.createTwoInst(dto1, dto2); - taskDto1.setTask_status(StatusEnum.TASK_RUNNING.getCode()); - taskDto1.setUpdate_time(DateUtil.now()); - this.update(taskDto1); - taskDto2.setTask_status(StatusEnum.TASK_RUNNING.getCode()); - taskDto2.setUpdate_time(DateUtil.now()); - this.update(taskDto2); - } + throw new BadRequestException("创建指令失败!"); +// TaskDto acsTask = this.findById(ids); +// if (acsTask == null) throw new BadRequestException("被删除或无权限,操作失败!"); +// List tasksByLinkNum = this.findByLinkNumFromCache(acsTask.getLink_num()); +// String link_no = CodeUtil.getNewCode("LINK_NO"); +// if (tasksByLinkNum.size() == 1) { +// TaskDto taskDto = tasksByLinkNum.get(0); +// Instruction instruction = instructionService.findByTaskCodeFromCache(taskDto.getTask_code()); +// if (instruction != null) { +// throw new BadRequestException("单任务有指令未完成!指令号:" + instruction.getInstruction_code()); +// } +// Instruction dto = instructionService.createInstDtoByTask(taskDto, link_no); +// instructionService.create(dto); +// taskDto.setTask_status(StatusEnum.TASK_RUNNING.getCode()); +// this.update(taskDto); +// } +// if (tasksByLinkNum.size() == 2) { +// TaskDto taskDto1 = tasksByLinkNum.get(0); +// Instruction inst1 = instructionService.findByTaskCodeFromCache(taskDto1.getTask_code()); +// if (inst1 != null) { +// throw new BadRequestException("双任务存有指令未完成!指令号:" + inst1.getInstruction_code()); +// } +// TaskDto taskDto2 = tasksByLinkNum.get(1); +// Instruction inst2 = instructionService.findByTaskCodeFromCache(taskDto2.getTask_code()); +// if (inst2 != null) { +// throw new BadRequestException("双任务有指令未完成!指令号:" + inst2.getInstruction_code()); +// } +// Instruction dto1 = instructionService.createInstDtoByTask(taskDto1, link_no); +// Instruction dto2 = instructionService.createInstDtoByTask(taskDto2, link_no); +// instructionService.createTwoInst(dto1, dto2); +// taskDto1.setTask_status(StatusEnum.TASK_RUNNING.getCode()); +// taskDto1.setUpdate_time(DateUtil.now()); +// this.update(taskDto1); +// taskDto2.setTask_status(StatusEnum.TASK_RUNNING.getCode()); +// taskDto2.setUpdate_time(DateUtil.now()); +// this.update(taskDto2); +// } } @Override diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/ItemsDataAccessor.java b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/ItemsDataAccessor.java new file mode 100644 index 00000000..6cfd1380 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/ItemsDataAccessor.java @@ -0,0 +1,17 @@ +package org.nl.acs.udw.mqttUdw; + +import org.nl.acs.udw.UnifiedData; +import org.nl.acs.udw.mqttUdw.service.ItemData; + +import java.util.List; + +/** + * @author onepiece + */ +public interface ItemsDataAccessor { + List getAllKey(); + + Object getValue(String key); + + void setValue(String key, Object value); +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/ItemsProcessService.java b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/ItemsProcessService.java new file mode 100644 index 00000000..11919509 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/ItemsProcessService.java @@ -0,0 +1,37 @@ +package org.nl.acs.udw.mqttUdw; + +import cn.hutool.core.util.ObjectUtil; +import org.nl.acs.udw.UnifiedData; +import org.nl.acs.udw.mqttUdw.service.ItemData; +import org.nl.acs.udw.mqttUdw.service.ItemUnit; +import org.nl.acs.udw.service.impl.UnifiedDataUnit; + +import java.util.List; + +/** + * @author onepiece + */ +public interface ItemsProcessService { + /** + * 获取所有的key + * + * @return + */ + List getAllUnifiedKey(); + + /** + * 根据key获取数据单元 + * + * @param key + * @return + */ + ItemUnit getItemUnit(String key); + + ItemData getItemData(String var1, String var2); + + Object getValue(String var1, String var2); + + void setValue(String var1, String var2, Object var3); + + List getAllDataKey(String var1); +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/factory/ItemDataAccessorFactory.java b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/factory/ItemDataAccessorFactory.java new file mode 100644 index 00000000..0d8e63d3 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/factory/ItemDataAccessorFactory.java @@ -0,0 +1,18 @@ +package org.nl.acs.udw.mqttUdw.factory; + +import org.nl.acs.udw.mqttUdw.ItemsDataAccessor; +import org.nl.acs.udw.mqttUdw.service.ItemsDataAccessorImpl; + +/** + * @author onepiece + */ +public class ItemDataAccessorFactory { + + private static final ItemsDataAccessorImpl itemsDataAccessor = new ItemsDataAccessorImpl(); + + public static ItemsDataAccessor getItemsDataAccessor(String unified_key) { + itemsDataAccessor.setUnifiedKey(unified_key); + itemsDataAccessor.setItemsProcess(ItemsProcessServiceFactory.getItemsUnifyProcess()); + return itemsDataAccessor; + } +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/factory/ItemsProcessServiceFactory.java b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/factory/ItemsProcessServiceFactory.java new file mode 100644 index 00000000..1f05a861 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/factory/ItemsProcessServiceFactory.java @@ -0,0 +1,17 @@ +package org.nl.acs.udw.mqttUdw.factory; + +import org.nl.acs.udw.mqttUdw.ItemsProcessService; +import org.nl.acs.udw.mqttUdw.service.ItemsProcessImpl; + +/** + * @author onepiece + */ +public class ItemsProcessServiceFactory { + + private static final ItemsProcessService itemsProcess = new ItemsProcessImpl(); + + + public static ItemsProcessService getItemsUnifyProcess() { + return itemsProcess; + } +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemData.java b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemData.java new file mode 100644 index 00000000..78d3f905 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemData.java @@ -0,0 +1,28 @@ +package org.nl.acs.udw.mqttUdw.service; + +import lombok.Data; + +import java.util.Date; + +/** + * @author onepiece + */ +@Data +public class ItemData { + private Object value; + private Date last_modify_date; + + public ItemData() { + this.last_modify_date = new Date(); + } + + public ItemData(Object value) { + this.value = value; + this.last_modify_date = new Date(); + } + + public void changeValue(Object value) { + this.value = value; + this.last_modify_date = new Date(); + } +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemUnit.java b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemUnit.java new file mode 100644 index 00000000..d92bc435 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemUnit.java @@ -0,0 +1,25 @@ +package org.nl.acs.udw.mqttUdw.service; + +import lombok.Data; +import org.nl.acs.udw.UnifiedData; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author onepiece + */ +@Data +public class ItemUnit { + private String unifiedKey; + private Map storage = new ConcurrentHashMap<>(); + private Map> history = new ConcurrentHashMap<>(); + + public ItemUnit(String unifiedKey) { + this.unifiedKey = unifiedKey; + } + +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemsDataAccessorImpl.java b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemsDataAccessorImpl.java new file mode 100644 index 00000000..ce037395 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemsDataAccessorImpl.java @@ -0,0 +1,39 @@ +package org.nl.acs.udw.mqttUdw.service; + +import org.nl.acs.udw.mqttUdw.ItemsDataAccessor; +import org.nl.acs.udw.mqttUdw.ItemsProcessService; + +import java.util.List; + +/** + * @author onepiece + */ +public class ItemsDataAccessorImpl implements ItemsDataAccessor { + + private String unified_key; + private ItemsProcessService itemsProcess; + + + public void setUnifiedKey(String unified_key) { + this.unified_key = unified_key; + } + + public void setItemsProcess(ItemsProcessService itemsProcess) { + this.itemsProcess = itemsProcess; + } + + @Override + public List getAllKey() { + return this.itemsProcess.getAllDataKey(this.unified_key); + } + + @Override + public Object getValue(String key) { + return this.itemsProcess.getValue(this.unified_key, key); + } + + @Override + public void setValue(String key, Object value) { + this.itemsProcess.setValue(this.unified_key, key, value); + } +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemsProcessImpl.java b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemsProcessImpl.java new file mode 100644 index 00000000..948eb9cd --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/udw/mqttUdw/service/ItemsProcessImpl.java @@ -0,0 +1,125 @@ +package org.nl.acs.udw.mqttUdw.service; + +import lombok.extern.slf4j.Slf4j; +import org.nl.acs.udw.UdwConfig; +import org.nl.acs.udw.UnifiedDataAppService; +import org.nl.acs.udw.mqttUdw.ItemsProcessService; +import org.nl.modules.common.exception.BadRequestException; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author onepiece + */ +@Slf4j +public class ItemsProcessImpl implements ItemsProcessService { + private Map factory = new ConcurrentHashMap<>(); + + public ItemsProcessImpl() { + } + + + @Override + public List getAllUnifiedKey() { + return new ArrayList(this.factory.keySet()); + } + + @Override + public ItemUnit getItemUnit(String unified_key) { + ItemUnit itemUnit = this.factory.get(unified_key); + return itemUnit == null ? null : itemUnit; + } + + @Override + public List getAllDataKey(String unified_key) { + ItemUnit itemUnit = (ItemUnit) this.factory.get(unified_key); + if (itemUnit == null) { + return new ArrayList(); + } else { + Map storage = itemUnit.getStorage(); + return new ArrayList(storage.keySet()); + } + } + + @Override + public ItemData getItemData(String unified_key, String key) { + ItemUnit itemUnit = this.getItemUnit(unified_key); + if (itemUnit == null) { + return null; + } else { + Map storage = itemUnit.getStorage(); + return (ItemData) storage.get(key); + } + } + + @Override + public Object getValue(String unified_key, String key) { + ItemData itemData = this.getItemData(unified_key, key); + return itemData == null ? null : itemData.getValue(); + } + + + @Override + public void setValue(String unified_key, String key, Object value) { + this.setValue(unified_key, key, value, false, true); + } + + + public synchronized void setValue(String unified_key, String key, Object value, boolean save, boolean is_log) { + if (unified_key == null) { + throw new BadRequestException(""); + //throw new BusinessException(SystemMessage.cant_be_empty, new Object[]{"unified_key"}); + } else if (key == null) { + throw new BadRequestException(""); + //throw new BusinessException(SystemMessage.cant_be_empty, new Object[]{"key"}); + } else { + if (!this.factory.containsKey(unified_key)) { + this.factory.put(unified_key, new ItemUnit(unified_key)); + } + + ItemUnit itemUnit = (ItemUnit) this.factory.get(unified_key); + Map storage = itemUnit.getStorage(); + if (!storage.containsKey(key)) { + storage.put(key, new ItemData()); + } + + ItemData itemData = (ItemData) storage.get(key); + if (!UnifiedDataAppService.isEquals(itemData.getValue(), value)) { + Map> history = itemUnit.getHistory(); + List historyItemDatas = (List) history.get(key); + if (historyItemDatas == null) { + history.put(key, new ArrayList()); + } + + ItemData historyData = new ItemData(); + historyData.setLast_modify_date(itemData.getLast_modify_date()); + historyData.setValue(itemData.getValue()); + + while (((List) history.get(key)).size() > UdwConfig.max_history_length) { + ((List) history.get(key)).remove(UdwConfig.max_history_length); + } + + ((List) history.get(key)).add(0, historyData); + Object oldvalue = itemData.getValue(); + itemData.changeValue(value); + if (save) { + /*PersistenceService persistenceService = PersistenceServiceFactory.getPersistenceService(); + persistenceService.saveData(unified_key, key, StringUtl.getString(value)); + if (is_log) { + this.businessLogger.setResource(unified_key, unified_key); + this.businessLogger.setMaterial(key, key); + this.businessLogger.setContainer(StringUtl.getString(value)); + this.businessLogger.log("统一数据源中: unit: {}, key: {}, 值: {} 更改为 {}。", new Object[]{unified_key, key, oldvalue, value}); + }*/ + } + + if (is_log && key != null && !key.endsWith("heartbeat") && !key.endsWith("distancex") && !key.endsWith("distancey") && !key.endsWith("Xwz") && !key.endsWith("Ywz") && !key.endsWith("Zwz")) { + log.trace("统一数据源中: unit: {}, key: {}, 值: {} 更改为 {}。", new Object[]{unified_key, key, oldvalue, value}); + } + } + + } + } + +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/ItemUtil.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/ItemUtil.java new file mode 100644 index 00000000..0eb2506a --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/ItemUtil.java @@ -0,0 +1,48 @@ +package org.nl.config.mqtt2; + +import org.nl.acs.device_driver.DeviceDriver; +import org.nl.acs.device_driver.driver.AbstractDeviceDriver; +import org.nl.acs.opc.Device; +import org.nl.acs.opc.DeviceAppService; +import org.nl.config.mqtt2.msg.DeviceItemData; +import org.nl.modules.wql.util.SpringContextHolder; + +import java.util.List; +import java.util.Optional; + +/** + * @Description TODO + * @Author Gengby + * @Date 2024/3/18 + */ +public class ItemUtil { + + /** + * 心跳item名称 + */ + private static final String HEARTBEAT = "heartbeat"; + + /** + * 获取指定符合索引 + * + * @param text itemId + * @param character 指定符合 + * @param n 第几个富豪 + * @return 索引 + */ + public static int nthIndexOf(String text, String character, int n) { + int position = -1; + do { + position = text.indexOf(character, position + 1); + } while (n-- > 1 && position != -1); + return position; + } + + public static boolean hasHeartbeat(String itemId) { + return itemId.contains(HEARTBEAT); + } + + public static void setIsOnline(DeviceAppService deviceAppService, String deviceCode, List items) { + } + +} \ No newline at end of file diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/MQServer.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/MQServer.java deleted file mode 100644 index e2c55619..00000000 --- a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/MQServer.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.nl.config.mqtt2; - -import org.apache.commons.lang3.StringUtils; -import org.eclipse.paho.client.mqttv3.MqttClient; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; -import org.nl.config.mqtt2.callback.PublishCallback; -import org.nl.config.mqtt2.callback.SubsribeCallback; -import org.nl.config.mqtt2.config.MqttConfig; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Service; - -import javax.annotation.PostConstruct; -import java.util.UUID; - -/* - * @author ZZQ - * @Date 2024/1/31 14:07 - * - */ -@Service -@ConditionalOnProperty(name = "mqtt.active", havingValue = "true") -public class MQServer { - - @Autowired - private MqttConfig mqttConfig; - - public static MqttClient subsribeClient; - - public static MqttClient publishClient; - - @PostConstruct - public void init() throws Exception { - this.initSubsribe(); - this.initPublish(); - } - - - public static void sendMsg(String topic,String body,int qos,Boolean retained){ - try { - if (publishClient!=null && StringUtils.isNotEmpty(body)){ - MqttMessage message = new MqttMessage(); - message.setQos(qos); - message.setRetained(retained); - message.setPayload(body.getBytes()); - publishClient.publish(topic,message); - } - }catch (Exception ex){ - ex.printStackTrace(); - } - } - - public void shutdown() throws Exception { - subsribeClient.disconnect(); - publishClient.disconnect(); - } - - private void initSubsribe() throws Exception { - if (subsribeClient!=null){ - System.out.println("重新连接"); - subsribeClient.disconnect(); - } - subsribeClient = new MqttClient(mqttConfig.getUrl(), mqttConfig.getClientId(), new MemoryPersistence()); - subsribeClient.connect(mqttConfig.getOption()); - subsribeClient.setCallback(new SubsribeCallback()); - subsribeClient.subscribe(mqttConfig.getTopics()); - } - private void initPublish() throws Exception { - if (publishClient!=null){ - System.out.println("重新连接"); - publishClient.disconnect(); - } - publishClient = new MqttClient(mqttConfig.getUrl(), UUID.randomUUID().toString(), new MemoryPersistence()); - publishClient.connect(mqttConfig.getOption()); - publishClient.setCallback(new PublishCallback()); - } -} diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/MqttServer.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/MqttServer.java new file mode 100644 index 00000000..010c39d3 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/MqttServer.java @@ -0,0 +1,412 @@ +package org.nl.config.mqtt2; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import io.micrometer.core.instrument.util.NamedThreadFactory; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.paho.client.mqttv3.*; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.nl.acs.auto.initial.ApplicationAutoInitial; +import org.nl.acs.device.service.OpcService; +import org.nl.acs.device.service.dto.OpcDto; +import org.nl.acs.device_driver.driver.AbstractDeviceDriver; +import org.nl.acs.opc.Device; +import org.nl.acs.opc.DeviceAppService; +import org.nl.acs.opc.OpcConfig; +import org.nl.acs.opc.OpcServerService; +import org.nl.acs.udw.mqttUdw.ItemsDataAccessor; +import org.nl.acs.udw.mqttUdw.factory.ItemDataAccessorFactory; +import org.nl.config.mqtt2.config.MqttConfig; +import org.nl.config.mqtt2.msg.DeviceItemData; +import org.nl.modules.lucene.service.LuceneExecuteLogService; +import org.nl.modules.lucene.service.dto.LuceneLogDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.*; +import java.util.concurrent.*; +import java.util.stream.Collectors; + +/** + * @Description TODO + * @Author Gengby + * @Date 2024/3/15 + */ +@Component +@ConditionalOnProperty(name = "mqtt.active", havingValue = "true") +@ConditionalOnBean({LuceneExecuteLogService.class, OpcServerService.class, DeviceAppService.class}) +@Slf4j +public class MqttServer implements MqttCallback, ApplicationAutoInitial { + + @Autowired + private MqttConfig mqttConfig; + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private LuceneExecuteLogService logService; + @Autowired + private OpcService opcService; + @Autowired + private DeviceAppService deviceAppService; + + /** + * 根据topic存储订阅消息 + */ + private final Map> topicMap = new ConcurrentHashMap<>(); + + /** + * 存储设备点位信息 + */ + private static final ItemsDataAccessor ACCESSOR_VALUE = ItemDataAccessorFactory.getItemsDataAccessor(OpcConfig.udw_opc_value_key); + + /** + * 发布TOPIC + */ + private static final String PUBLISH_TOPIC = "publishTopic"; + + /** + * 重连开启一个新的线程 + */ + private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + + /** + * 重连延迟时间,单位:毫秒 + */ + private static final long RECONNECT_DELAY = 5000L; + + /** + * 重连标记 + */ + private volatile boolean reconnectingFlag = false; + + /** + * 存储Redis数据Key + */ + private static final String KEY = "opc:items"; + + /** + * 心跳item名称 + */ + private static final String HEARTBEAT = "heartbeat"; + + /** + * MQTT客户端 + */ + private MqttClient mqttClient; + + /** + * MQTT QOS级别 这里默认的使用的是QOS_2 + * 0 最多交付一次 可能导致消息丢失 + * 1 最少交付一次 可能导致消息重复 + * 2 只交付一次 保证消息不会丢失和重复 + */ + private static final int QOS_0 = 0; + private static final int QOS_1 = 1; + private static final int QOS_2 = 2; + + /** + * 线程池核心线程数量 + */ + private static final int CORE_POOL_SIZE = 1; + + /** + * 线程池最大线程数量 + */ + private static final int MAX_POOL_SIZE = 10; + + /** + * 非核心线程存活时间 + */ + private static final int KEEP_ALIVE_TIME = 10; + + /** + * 用来存储等待执行任务的阻塞队列的大小 + */ + private static final int WORK_QUEUE_SIZE = 100; + + /** + * 队列 + */ + private static final Queue MSG_QUEUE = new LinkedBlockingQueue<>(); + + /** + * 线程工厂 + */ + private final ThreadFactory threadFactory = new NamedThreadFactory("MqttThreadFactory"); + + /** + * 线程池 + */ + private final ThreadPoolExecutor threadPool = new ThreadPoolExecutor( + CORE_POOL_SIZE, + MAX_POOL_SIZE, + KEEP_ALIVE_TIME, + TimeUnit.SECONDS, + new ArrayBlockingQueue<>(WORK_QUEUE_SIZE), + threadFactory, + (r, executor) -> MSG_QUEUE.add(r) + ); + + + @Override + public void autoInitial() throws Exception { + this.init(); + } + + + private void init() throws Exception { + this.initTopicMap(); + this.initMqttClient(); + this.run(); + } + + /** + * 初始化topicMap + *

+ * 从数据库中查询所有的topic + * 并为每个topic初始化一个队列,分别存储其订阅的消息 + */ + private void initTopicMap() { + log.info("init topicMap..."); + List opcDtos = opcService.queryAll(null); + //根据配置OPC信息动态调整核心线程池个数 + threadPool.setCorePoolSize(opcDtos.size() + 1); + threadPool.setMaximumPoolSize(opcDtos.size() + 10); + opcDtos.forEach(opcDto -> topicMap.put(opcDto.getTopic(), new LinkedBlockingQueue<>())); + log.info("init topicMap finish"); + } + + /** + * 初始化MQTT客户端 + * + * @throws Exception + */ + private void initMqttClient() throws Exception { + log.info("init mqtt client..."); + if (mqttClient != null) { + mqttClient.disconnect(); + } + mqttClient = new MqttClient(mqttConfig.getUrl(), mqttConfig.getClientId(), new MemoryPersistence()); + mqttClient.connect(mqttConfig.getOption()); + mqttClient.setCallback(this); + mqttClient.subscribe(mqttConfig.getTopics(), mqttConfig.getQoss()); + log.info("init mqtt client finish"); + } + + + private void run() { + topicMap.forEach((topic, values) -> threadPool.execute(() -> { + try { + log.info("create topic thread:{}", topic); + while (true) { + String message = values.take(); + execute(topic, message); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + log.error("topic:{} , msg handle failed: {}", topic, e.getMessage()); + } + })); + } + + + private void execute(String topic, String body) { + JSONObject msg = JSONObject.parseObject(body); + JSONArray msgValues = msg.getJSONArray("values"); + List itemDataList = msgValues.toJavaList(DeviceItemData.class); + Map> map = itemDataList.stream().collect(Collectors.groupingBy(deviceData -> { + int thirdDotIndex = ItemUtil.nthIndexOf(deviceData.getId(), ".", 3); + return deviceData.getId().substring(0, thirdDotIndex); + })); + map.forEach((itemId, items) -> { + int startIndex = ItemUtil.nthIndexOf(itemId, ".", 2) + 1; + items.stream().anyMatch(item -> ItemUtil.hasHeartbeat(item.getId())); + String deviceCode = itemId.substring(startIndex); + Optional vOptional = items.stream() + .filter(item -> item.getId().contains(HEARTBEAT)) + .map(DeviceItemData::isQ) + .findFirst(); + vOptional.ifPresent(q -> { + Device device = deviceAppService.findDeviceByCode(deviceCode); + if (device != null && device.getDeviceDriver() instanceof AbstractDeviceDriver) { + AbstractDeviceDriver deviceDriver = (AbstractDeviceDriver) device.getDeviceDriver(); + if (!Objects.equals(deviceDriver.getOnline(), q)) { + deviceDriver.setOnline(q); + log.info("device:{},online status:{}", deviceCode, q); + } + } + }); + setValue(deviceCode, items); + }); + System.out.println("线程名称:'" + Thread.currentThread() + "',接收到消息" + topic + "-" + body + "-"); + } + + + /** + * 更改内存中的值 + * + * @param deviceCode + * @param value + */ + private void setValue(String deviceCode, List value) { + value.forEach(deviceData -> { + logService.deviceExecuteLog(new LuceneLogDto(deviceCode, "MQTT上报,信号:" + deviceData.getId() + ",由" + ACCESSOR_VALUE.getValue(deviceData.getId()) + "->" + deviceData.getV() + ",信号健康值:" + deviceData.isQ())); + ACCESSOR_VALUE.setValue(deviceData.getId(), deviceData.getV()); + }); + } + + + /** + * 断开连接执行逻辑 + * + * @param cause + */ + @Override + public void connectionLost(Throwable cause) { + log.info("mqtt client break link"); + if (!reconnectingFlag) { + reconnectingFlag = true; + scheduler.execute(() -> { + while (!mqttClient.isConnected()) { + try { + if (!mqttClient.isConnected()) { + mqttClient.reconnect(); + log.info("mqtt client again success"); + } + } catch (MqttException e) { + log.error("mqtt client again failed: {}", e.getMessage()); + try { + Thread.sleep(RECONNECT_DELAY); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } + reconnectingFlag = false; + }); + } + } + + + /** + * 订阅topic + * + * @param topic 主题 + * @param message 内容 + * @throws Exception + */ + @Override + public void messageArrived(String topic, MqttMessage message) throws Exception { + //接收到消息:先放队列,根据不同topic处理不同处理dd逻辑 + if (topicMap.containsKey(topic)) { + topicMap.get(topic).add(new String(message.getPayload())); + System.out.println("接收到消息---" + topic + "内容:" + new String(message.getPayload())); + } else { + log.info("topic---{} does not exist", topic); + } + } + + + /** + * 发布结果处理 + * + * @param token + */ + @SneakyThrows + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + boolean complete = token.isComplete(); + MqttMessage message = token.getMessage(); + String[] topics = token.getTopics(); + log.info("message publish result ,topic:{},message:{},complete:{}", Arrays.toString(topics), new String(message.getPayload()), complete); + } + + /** + * 消息发布 + * + * @param body MQTT下发数据的格式是 + * [ + * {"id":"opc_code.plc_code.device_code.item","v":value}, + * {"id":"opc_code.plc_code.device_code.item","v":value} + * ] + */ + public void sendMsg(String body) { + try { + synchronized (this) { + if (mqttClient != null && StringUtils.isNotEmpty(body)) { + MqttMessage message = new MqttMessage(); + message.setQos(QOS_2); + message.setRetained(true); + message.setPayload(body.getBytes()); + mqttClient.publish(PUBLISH_TOPIC, message); + } + } + } catch (Exception ex) { + log.error("message publish failed:{}", ex.getMessage()); + } + } + + + @PostConstruct + public void start() { + loadItems(); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + System.out.println("----close threadPool-----"); + try { + shutdown(); + unloadItems(); + threadPool.shutdown(); + } catch (Exception e) { + e.printStackTrace(); + } + })); + } + + /** + * 关闭MQTT客户端连接 + * + * @throws Exception + */ + public void shutdown() throws Exception { + log.info("close mqtt client..."); + mqttClient.disconnect(); + log.info("close mqtt client finish"); + + log.info("close scheduler..."); + scheduler.shutdown(); + log.info("close scheduler finish..."); + } + + /** + * 从redis写入内存 item数据 + */ + private void loadItems() { + log.info("loading..."); + Map entries = redisTemplate.opsForHash().entries(KEY); + entries.forEach((key, value) -> ACCESSOR_VALUE.setValue((String) key, value)); + log.info("load finish, delete key..."); + redisTemplate.delete(KEY); + log.info("delete finish!"); + } + + /** + * 从内存写出到redis item数据 + */ + private void unloadItems() { + log.info("unloading..."); + List allKey = ACCESSOR_VALUE.getAllKey(); + Map map = new HashMap<>(); + allKey.forEach(key -> map.put(key, ACCESSOR_VALUE.getValue(key))); + log.info("unload finish, put key..."); + redisTemplate.opsForHash().putAll(KEY, map); + log.info("put finish!"); + } + +} \ No newline at end of file diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/PublishDemo.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/PublishDemo.java deleted file mode 100644 index 995a0bb1..00000000 --- a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/PublishDemo.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.nl.config.mqtt2; - -import cn.dev33.satoken.annotation.SaIgnore; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; - -/* - * @author ZZQ - * @Date 2024/1/31 17:12 - */ -//@RestController -public class PublishDemo { - - @Autowired - MQServer server; - - @RequestMapping("/publish") - @SaIgnore - public void send(String topic,String msg){ - MQServer.sendMsg(topic,msg,1,true); - } - - @RequestMapping("/init") - @SaIgnore - public void d222() throws Exception { - server.init(); - } -} diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/callback/PublishCallback.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/callback/PublishCallback.java deleted file mode 100644 index 023db756..00000000 --- a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/callback/PublishCallback.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.nl.config.mqtt2.callback; - -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttMessage; - -/* - * @author ZZQ - * @Date 2024/1/31 13:55 - */ -public class PublishCallback implements MqttCallback { - - @Override - public void connectionLost(Throwable throwable) { - //重连,调用服务初始化init - } - - @Override - public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { - //接收到消息:先放队列,根据不同topic处理不同处理逻辑 - } - - @Override - public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { - //消息接收成功后 - boolean complete = iMqttDeliveryToken.isComplete(); - System.out.println("消息处理结果:"+complete); - } -} diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/callback/SubsribeCallback.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/callback/SubsribeCallback.java deleted file mode 100644 index 68316daf..00000000 --- a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/callback/SubsribeCallback.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.nl.config.mqtt2.callback; - -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.nl.config.mqtt2.msg.MsgPoolManager; -import org.nl.config.mqtt2.msg.MsgWorker; - -/* - * @author ZZQ - * @Date 2024/1/31 13:55 - */ -public class SubsribeCallback implements MqttCallback { - - @Override - public void connectionLost(Throwable throwable) { - //重连,调用服务初始化init - } - - @Override - public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { - //接收到消息:先放队列,根据不同topic处理不同处理dd逻辑 - System.out.println("接收到消息topic:"+topic+"——"+new String(mqttMessage.getPayload())); - MsgPoolManager.hander(new MsgWorker(topic,new String(mqttMessage.getPayload()))); - } - - @Override - public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { - //消息接收如果处理失败允许在这里重试 - } -} diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/config/MqttConfig.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/config/MqttConfig.java index 4ab39b02..c009e251 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/config/MqttConfig.java +++ b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/config/MqttConfig.java @@ -4,7 +4,14 @@ import lombok.Data; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.integration.dsl.IntegrationFlow; +import org.springframework.integration.dsl.IntegrationFlows; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.core.MqttPahoClientFactory; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; import javax.annotation.PostConstruct; @@ -29,6 +36,8 @@ public class MqttConfig { private String[] topics; + private int[] qoss; + private int timeout; private int keepalive; @@ -36,23 +45,22 @@ public class MqttConfig { private MqttConnectOptions option; @PostConstruct - public void initOption(){ + public void initOption() { MqttConnectOptions options = new MqttConnectOptions(); // 设定清除会话信息,true时,每次连接都会建立新的会话,false时,服务端会保留会话信息 - options.setCleanSession(true); - //options.setCleanSession(true); + options.setCleanSession(false); // 设定重连机制,设定为true时,mqtt的重连机制会启动,当mqtt client掉线之后它会进入重连 options.setAutomaticReconnect(true); - if (!username.equals("a")){ - options.setUserName(username); - } - if (!password.equals("a")){ - options.setPassword(password.toCharArray()); - } + options.setUserName(username); + options.setPassword(password.toCharArray()); options.setConnectionTimeout(timeout); options.setKeepAliveInterval(keepalive); + // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。 //options.setWill("willTopic", WILL_DATA, 2, false); - option=options; + option = options; } + + + } diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/DeviceItemData.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/DeviceItemData.java new file mode 100644 index 00000000..37f73e93 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/DeviceItemData.java @@ -0,0 +1,18 @@ +package org.nl.config.mqtt2.msg; + +import lombok.Data; + +import java.util.List; + +/** + * @Description TODO + * @Author Gengby + * @Date 2024/3/5 + */ +@Data +public class DeviceItemData { + private String id; + private T v; + private boolean q; + private long t; +} \ No newline at end of file diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/MsgPoolManager.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/MsgPoolManager.java deleted file mode 100644 index 5d9ba37c..00000000 --- a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/MsgPoolManager.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.nl.config.mqtt2.msg; - -import org.nl.config.mqtt2.MQServer; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.util.Queue; -import java.util.concurrent.*; - -/* - * @author ZZQ - * @Date 2024/1/31 15:19 - * 待确认:1.所有消走线程还是先走队列;2.同一个topic消息处理需要保证消费顺序3.消息写时订阅也会订阅毁掉会拿到自己写的信息 - */ -@Component -@ConditionalOnBean(value = MQServer.class) -public class MsgPoolManager { - - @Autowired - MQServer mqServer; - // 线程池维护线程的最少数量 - private final static int CORE_POOL_SIZE = 2; - // 线程池维护线程的最大数量 - private final static int MAX_POOL_SIZE = 20 ; - // 线程池维护线程所允许的空闲时间 - private final static int KEEP_ALIVE_TIME = 10; - // 线程池 所使用的缓存队列大小 - private final static int WORK_QUEUE_SIZE = 1000; - - - private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5); - - - static Queue msgQueue = new LinkedBlockingQueue<>(); - - static ThreadPoolExecutor threadPool =null; - - static ScheduledFuture scheduledFuture =null; - - static { - - } - - - public static void hander(MsgWorker r){ - threadPool.execute(r); - } - - @PostConstruct - public void start() { - threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,MAX_POOL_SIZE, - KEEP_ALIVE_TIME, TimeUnit.SECONDS,new ArrayBlockingQueue<>(WORK_QUEUE_SIZE), (r, executor) -> msgQueue.add(r)); - -// scheduledFuture = scheduler.scheduleAtFixedRate(() -> { -// // 判断缓存队列是否存在记录 -// System.out.println("定时任务启动"+msgQueue.size()); -// if(!msgQueue.isEmpty()){ -// if(threadPool.getQueue().size() < WORK_QUEUE_SIZE){ -// Runnable worker = msgQueue.poll(); -// threadPool.execute(worker); -// } -// } -// },0,1,TimeUnit.SECONDS); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - System.out.println("----关闭线程池-----"); - try { - mqServer.shutdown(); - threadPool.shutdown(); -// scheduler.shutdown(); - } catch (Exception e) { - e.printStackTrace(); - } - })); - } - -} diff --git a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/MsgWorker.java b/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/MsgWorker.java deleted file mode 100644 index d826fad7..00000000 --- a/wcs/nladmin-system/src/main/java/org/nl/config/mqtt2/msg/MsgWorker.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.nl.config.mqtt2.msg; - -/* - * @author ZZQ - * @Date 2024/1/31 15:29 - * 消息处理线程:通过线程池处理:如果需要用到具体topic的handler:可以传入 - */ -public class MsgWorker implements Runnable{ - /** - * topic - */ - public String topic; - /** - * 消息体 - */ - private String body; - - @Override - public void run() { - System.out.println("接收到消息"+topic+"-"+body); - } - - public MsgWorker(String topic, String body) { - this.topic = topic; - this.body = body; - } -} diff --git a/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateAgvOneInst.java b/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateAgvOneInst.java index 8ffb2290..5209d728 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateAgvOneInst.java +++ b/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateAgvOneInst.java @@ -5,7 +5,6 @@ import org.nl.acs.acsEnum.StatusEnum; import org.nl.acs.instruction.service.InstructionService; import org.nl.acs.task.service.TaskService; import org.nl.acs.task.service.dto.TaskDto; -import org.nl.modules.system.util.CodeUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -32,14 +31,13 @@ public class AutoCreateAgvOneInst { * @throws Exception */ public void run() throws Exception { - List list = taskService.queryAll("task_status = '0' and (agv_system_type = '2' or agv_system_type = '3')"); + List list = taskService.queryAll("task_status = '0' and agv_system_type = '2'"); for (int i = 0; i < list.size(); i++) { TaskDto taskDto = list.get(i); - String link_no = CodeUtil.getNewCode("LINK_NO"); try { - instructionService.create(instructionService.createInstDtoByTask(taskDto, link_no)); + instructionService.autoCreateOneInst(instructionService.createInstDtoByTask(taskDto)); } catch (Exception e) { - log.error("自动创建指令失败:任务号{},失败原因:{}", taskDto.getTask_code(), e.getMessage()); + log.error("自动创建叉车指令失败:任务号{},失败原因:{}", taskDto.getTask_code(), e.getMessage()); taskDto.setRemark(e.getMessage()); taskService.updateByCodeFromCache(taskDto); continue; @@ -47,7 +45,7 @@ public class AutoCreateAgvOneInst { //创建指令后修改任务状态 taskDto.setTask_status(StatusEnum.TASK_RUNNING.getCode()); taskService.update(taskDto); - log.info("自动创建指令成功,任务号:{}", taskDto.getTask_code()); + log.info("自动创建叉车指令成功,任务号:{}", taskDto.getTask_code()); } } } diff --git a/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateHrInst.java b/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateHrInst.java new file mode 100644 index 00000000..bcc0db43 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateHrInst.java @@ -0,0 +1,60 @@ +package org.nl.modules.quartz.task; + +import lombok.extern.slf4j.Slf4j; +import org.nl.acs.acsEnum.StatusEnum; +import org.nl.acs.instruction.service.InstructionService; +import org.nl.acs.task.service.TaskService; +import org.nl.acs.task.service.dto.TaskDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 自动创建海柔AGV指令 + * + * @author GengBy + */ +@Slf4j +@Component +public class AutoCreateHrInst { + + @Autowired + private InstructionService instructionService; + @Autowired + private TaskService taskService; + + /** + * 海柔最大指令数量 + */ + private static final int MAX_INST_NUM = 3; + + /** + * 查询海柔AGV任务并生成对应的指令 + * + * @throws Exception + */ + public void run() throws Exception { + List list = taskService.queryAll("task_status = '0' and agv_system_type = '3'"); + for (int i = 0; i < list.size(); i++) { + TaskDto taskDto = list.get(i); + try { + int maxNum = instructionService.querySameDeviceCodeForHrMaxNum(taskDto.getStart_point_code(), taskDto.getNext_point_code()); + if (maxNum == MAX_INST_NUM) { + log.info("海柔同一点位指令数量大于允许生成的最大指令数量:" + MAX_INST_NUM); + continue; + } + instructionService.autoCreateHrInst(instructionService.createInstDtoByTask(taskDto)); + } catch (Exception e) { + log.error("自动创建海柔指令失败:任务号{},失败原因:{}", taskDto.getTask_code(), e.getMessage()); + taskDto.setRemark(e.getMessage()); + taskService.updateByCodeFromCache(taskDto); + continue; + } + //创建指令后修改任务状态 + taskDto.setTask_status(StatusEnum.TASK_RUNNING.getCode()); + taskService.update(taskDto); + log.info("自动创建海柔指令成功,任务号:{}", taskDto.getTask_code()); + } + } +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateInst.java b/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateInst.java index 46fa5662..c3d5e6c5 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateInst.java +++ b/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateInst.java @@ -114,7 +114,7 @@ public class AutoCreateInst { //生产指令关联编号 String link_no = CodeUtil.getNewCode("LINK_NO"); - Instruction instDto = instructionService.createInstDtoByTask(taskDto, link_no); + Instruction instDto = instructionService.createInstDtoByTask(taskDto); try { instructionService.create(instDto); @@ -152,9 +152,8 @@ public class AutoCreateInst { //生产指令关联编号 String link_no = CodeUtil.getNewCode("LINK_NO"); - - Instruction instDto1 = instructionService.createInstDtoByTask(taskDto1, link_no); - Instruction instDto2 = instructionService.createInstDtoByTask(taskDto2, link_no); + Instruction instDto1 = instructionService.createInstDtoByTask(taskDto1); + Instruction instDto2 = instructionService.createInstDtoByTask(taskDto2); try { instructionService.createTwoInst(instDto1, instDto2); diff --git a/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateNuoBaoInst.java b/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateNuoBaoInst.java new file mode 100644 index 00000000..7b01e149 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/modules/quartz/task/AutoCreateNuoBaoInst.java @@ -0,0 +1,130 @@ +package org.nl.modules.quartz.task; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.nl.acs.acsEnum.StatusEnum; +import org.nl.acs.instruction.service.InstructionService; +import org.nl.acs.instruction.service.dto.Instruction; +import org.nl.acs.opc.DeviceAppService; +import org.nl.acs.task.service.TaskService; +import org.nl.acs.task.service.dto.TaskDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 自动创建诺宝AGV指令 + * + * @author GengBy + */ +@Slf4j +@Component +public class AutoCreateNuoBaoInst { + + @Autowired + private TaskService taskService; + @Autowired + private InstructionService instructionService; + @Autowired + private DeviceAppService deviceAppService; + + + /** + * 单任务个数 + */ + public static final Integer TASK_SIZE_1 = 1; + /** + * 双任务个数 + */ + public static final Integer TASK_SIZE_2 = 2; + + /** + * 自动创建诺宝AGV指令 + */ + public void run() throws Exception { + + //查询所有诺宝AGV任务 + List list = taskService.queryAll("task_status = '0' and agv_system_type = '1' "); + + if (ObjectUtil.isEmpty(list)) { + return; + } + //根据LINK_NUM分组任务 + Map> link_num_tasks = Optional + .ofNullable(list) + .orElse(new ArrayList<>()) + .stream() + .collect(Collectors.groupingBy(TaskDto::getLink_num)); + //遍历分组任务 如果任务数为2则是下发双任务,如果任务数为1 则下发单任务 + for (Map.Entry> task_values : link_num_tasks.entrySet()) { + List tasks = + Optional + .ofNullable(task_values.getValue()) + .orElse(new ArrayList<>()) + .stream() + .sorted((t1, t2) -> t1.getIs_send().compareTo(t2.getIs_send())) + .collect(Collectors.toList()); + + + //如果分组后任务数量为1 下发单任务,分组后任务数量为2 下发双任务 + if (tasks.size() == TASK_SIZE_1) { + TaskDto taskDto = tasks.get(0); + //判断任务是否需要立即下发AGV 不需要则是双任务,等待系统下发第二条任务后,走下发双任务的逻辑 + if (StrUtil.equals(taskDto.getIs_send(), StatusEnum.NO_SEND.getCode())) { + continue; + } + + Instruction instDto = instructionService.createInstDtoByTask(taskDto); + + try { + instructionService.autoCreateTwoInst(instDto, null); + } catch (Exception e) { + taskDto.setRemark(e.getMessage()); + taskService.updateByCodeFromCache(taskDto); + continue; + } + + //创建指令后修改任务状态 + taskDto.setTask_status(StatusEnum.TASK_RUNNING.getCode()); + taskService.update(taskDto); + + } else if (tasks.size() == TASK_SIZE_2) { + + TaskDto taskDto1 = tasks.get(0); + TaskDto taskDto2 = tasks.get(1); + + //关联编号一致 任务类型不一致 不生成任务 + if (!taskDto1.getTask_type().equals(taskDto2.getTask_type())) { + continue; + } + + Instruction instDto1 = instructionService.createInstDtoByTask(taskDto1); + Instruction instDto2 = instructionService.createInstDtoByTask(taskDto2); + + try { + instructionService.autoCreateTwoInst(instDto1, instDto2); + } catch (Exception e) { + taskDto1.setRemark(e.getMessage()); + taskService.updateByCodeFromCache(taskDto1); + taskDto2.setRemark(e.getMessage()); + taskService.updateByCodeFromCache(taskDto2); + continue; + } + + //创建指令后修改任务状态 + taskDto1.setTask_status(StatusEnum.TASK_RUNNING.getCode()); + taskService.update(taskDto1); + taskDto2.setTask_status(StatusEnum.TASK_RUNNING.getCode()); + taskService.update(taskDto2); + + } + } + } + +}