正在显示
68 个修改的文件
包含
3977 行增加
和
59 行删除
lh-jar/lh-device-mqtt-terminal-jar/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>lh-jar</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + </parent> | ||
| 11 | + | ||
| 12 | + <artifactId>lh-device-mqtt-terminal-jar</artifactId> | ||
| 13 | + | ||
| 14 | + <properties> | ||
| 15 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 16 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 17 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 18 | + </properties> | ||
| 19 | + <dependencies> | ||
| 20 | + <dependency> | ||
| 21 | + <groupId>org.slf4j</groupId> | ||
| 22 | + <artifactId>slf4j-api</artifactId> | ||
| 23 | + </dependency> | ||
| 24 | + | ||
| 25 | + <dependency> | ||
| 26 | + <groupId>ch.qos.logback</groupId> | ||
| 27 | + <artifactId>logback-classic</artifactId> | ||
| 28 | + </dependency> | ||
| 29 | + <dependency> | ||
| 30 | + <groupId>com.alibaba</groupId> | ||
| 31 | + <artifactId>fastjson</artifactId> | ||
| 32 | + </dependency> | ||
| 33 | + | ||
| 34 | + <!-- mqtt --> | ||
| 35 | + <dependency> | ||
| 36 | + <groupId>org.eclipse.paho</groupId> | ||
| 37 | + <artifactId>org.eclipse.paho.client.mqttv3</artifactId> | ||
| 38 | + </dependency> | ||
| 39 | + | ||
| 40 | + </dependencies> | ||
| 41 | +</project> |
| 1 | +package com.zhonglai.luhui.device.mqtt.terminal.jar; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.dto.Topic; | ||
| 4 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt.MqttService; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 数据解析工厂 | ||
| 8 | + */ | ||
| 9 | +public interface ParseDataFactory { | ||
| 10 | + void parse( Topic topicDto,byte[] paylaod, MqttService mqttService); | ||
| 11 | +} |
| 1 | +package com.zhonglai.luhui.device.mqtt.terminal.jar.config; | ||
| 2 | + | ||
| 3 | +import java.io.FileReader; | ||
| 4 | +import java.util.HashSet; | ||
| 5 | +import java.util.Properties; | ||
| 6 | +import java.util.Set; | ||
| 7 | + | ||
| 8 | +public class MqttConfig { | ||
| 9 | + public static String mqttBroker; | ||
| 10 | + public static String mqttUserName; | ||
| 11 | + public static String mqttPassword; | ||
| 12 | + public static String mqttClientId; | ||
| 13 | + | ||
| 14 | + public static Set<String> subTopic; | ||
| 15 | + | ||
| 16 | + public static void init(String path) | ||
| 17 | + { | ||
| 18 | + //加载properties配置文件 | ||
| 19 | + Properties properties = loadProperties(path); | ||
| 20 | + | ||
| 21 | + mqttBroker = properties.getProperty("mqtt.broker"); | ||
| 22 | + mqttUserName = properties.getProperty("mqtt.username"); | ||
| 23 | + mqttPassword = properties.getProperty("mqtt.password"); | ||
| 24 | + mqttClientId = properties.getProperty("mqtt.clientId"); | ||
| 25 | + subTopic = new HashSet<>(); | ||
| 26 | + | ||
| 27 | + String topicsStr = properties.getProperty("mqtt.subTopic"); | ||
| 28 | + if(null != topicsStr && !"".equals(topicsStr)) | ||
| 29 | + { | ||
| 30 | + if (topicsStr != null && !topicsStr.trim().isEmpty()) { | ||
| 31 | + String[] topics = topicsStr.split(","); | ||
| 32 | + for (String topic : topics) { | ||
| 33 | + subTopic.add(topic.trim()); | ||
| 34 | + } | ||
| 35 | + } | ||
| 36 | + } | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + private static Properties loadProperties(String path) | ||
| 40 | + { | ||
| 41 | + Properties properties = new Properties(); | ||
| 42 | + try { | ||
| 43 | + if(null != path && !"".equals(path)) | ||
| 44 | + { | ||
| 45 | + properties.load(new FileReader(path+"/mqtt.properties")); | ||
| 46 | + }else{ | ||
| 47 | + properties.load(MqttConfig.class.getClassLoader().getResourceAsStream("configs/mqtt.properties")); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + } catch (Exception e) { | ||
| 51 | + throw new RuntimeException("加载mqtt.properties失败,未找到配置文件或内容为空"); | ||
| 52 | + } | ||
| 53 | + return properties; | ||
| 54 | + } | ||
| 55 | +} |
| 1 | +package com.zhonglai.luhui.device.mqtt.terminal.jar.dto; | ||
| 2 | + | ||
| 3 | +public class Topic { | ||
| 4 | + private String topicType; | ||
| 5 | + private String time; | ||
| 6 | + public Topic() | ||
| 7 | + { | ||
| 8 | + | ||
| 9 | + } | ||
| 10 | + public Topic(String stopicStr) | ||
| 11 | + { | ||
| 12 | + String[] strs = stopicStr.split("/"); | ||
| 13 | + if (strs.length >= 2) { | ||
| 14 | + topicType = strs[0]; | ||
| 15 | + time = strs[1]; | ||
| 16 | + } else { | ||
| 17 | + topicType = stopicStr; // 或者给默认值 | ||
| 18 | + time = ""; | ||
| 19 | + } | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + public String getTopicType() { | ||
| 23 | + return topicType; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + public void setTopicType(String topicType) { | ||
| 27 | + this.topicType = topicType; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public String getTime() { | ||
| 31 | + return time; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + public void setTime(String time) { | ||
| 35 | + this.time = time; | ||
| 36 | + } | ||
| 37 | +} |
| 1 | +package com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.ParseDataFactory; | ||
| 5 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.dto.Topic; | ||
| 6 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
| 7 | +import org.eclipse.paho.client.mqttv3.MqttCallbackExtended; | ||
| 8 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
| 9 | +import org.slf4j.Logger; | ||
| 10 | +import org.slf4j.LoggerFactory; | ||
| 11 | + | ||
| 12 | +import java.util.Set; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * mqtt回调 | ||
| 16 | + */ | ||
| 17 | +public class MqttCallback implements MqttCallbackExtended { | ||
| 18 | + private final Logger log = LoggerFactory.getLogger(this.getClass()); | ||
| 19 | + | ||
| 20 | + private final MqttService mqttclient; | ||
| 21 | + private final ParseDataFactory parseDataFactory; | ||
| 22 | + | ||
| 23 | + public MqttCallback(MqttService mqttService, ParseDataFactory parseDataFactory) { | ||
| 24 | + this.mqttclient = mqttService; | ||
| 25 | + this.parseDataFactory = parseDataFactory; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + @Override | ||
| 29 | + public void connectComplete(boolean b, String s) { | ||
| 30 | + log.info("连接成功"); | ||
| 31 | + Set<String> topics = mqttclient.subscribe(); | ||
| 32 | + log.info("-----------订阅成功:{}--------------------",topics); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + public void connectionLost(Throwable cause) { | ||
| 37 | + log.error("连接丢失重新链接",cause); | ||
| 38 | + while (!mqttclient.connect()) | ||
| 39 | + { | ||
| 40 | + try { | ||
| 41 | + Thread.sleep(60000); | ||
| 42 | + } catch (InterruptedException e) { | ||
| 43 | + e.printStackTrace(); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @Override | ||
| 49 | + public void messageArrived(String topic, MqttMessage message) throws Exception { | ||
| 50 | + log.info("mqtt发来消息:topic={} payload={}", topic, message.toString()); | ||
| 51 | + Topic topicDto = new Topic(topic); | ||
| 52 | + parseDataFactory.parse(topicDto, message.getPayload(),mqttclient); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + @Override | ||
| 56 | + public void deliveryComplete(IMqttDeliveryToken token) { | ||
| 57 | + log.info("消息发送完成"); | ||
| 58 | + } | ||
| 59 | +} |
| 1 | +package com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.ParseDataFactory; | ||
| 4 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.config.MqttConfig; | ||
| 5 | +import org.eclipse.paho.client.mqttv3.MqttClient; | ||
| 6 | +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | ||
| 7 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
| 8 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
| 9 | +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; | ||
| 10 | +import org.slf4j.Logger; | ||
| 11 | +import org.slf4j.LoggerFactory; | ||
| 12 | + | ||
| 13 | +import javax.annotation.PreDestroy; | ||
| 14 | +import java.util.Set; | ||
| 15 | + | ||
| 16 | +public class MqttService { | ||
| 17 | + private final Logger log = LoggerFactory.getLogger(this.getClass()); | ||
| 18 | + | ||
| 19 | + private MqttClient mqttclient; | ||
| 20 | + private MqttConnectOptions options; | ||
| 21 | + | ||
| 22 | + // 使用 volatile 确保多线程内存可见性和禁止指令重排 | ||
| 23 | + private static volatile MqttService instance; | ||
| 24 | + | ||
| 25 | + private ParseDataFactory parseDataFactory; | ||
| 26 | + | ||
| 27 | + private MqttService() { | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public static MqttService getInstance() { | ||
| 31 | + if (instance == null) { | ||
| 32 | + synchronized (MqttService.class) { | ||
| 33 | + if (instance == null) { | ||
| 34 | + instance = new MqttService(); | ||
| 35 | + } | ||
| 36 | + } | ||
| 37 | + } | ||
| 38 | + return instance; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + private void init() throws MqttException { | ||
| 42 | + if(null == mqttclient) | ||
| 43 | + { | ||
| 44 | + mqttclient = new MqttClient(MqttConfig.mqttBroker, MqttConfig.mqttClientId, new MemoryPersistence()); | ||
| 45 | + } | ||
| 46 | + options = new MqttConnectOptions(); | ||
| 47 | + options.setCleanSession(true); | ||
| 48 | + options.setConnectionTimeout(15); | ||
| 49 | + options.setKeepAliveInterval(60); // 添加心跳设置,单位为秒 | ||
| 50 | + //设置断开后重新连接 | ||
| 51 | + options.setAutomaticReconnect(true); | ||
| 52 | + mqttclient.setCallback(new MqttCallback(this,parseDataFactory)); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + public boolean connect() { | ||
| 56 | + try { | ||
| 57 | + options.setUserName(MqttConfig.mqttUserName); | ||
| 58 | + options.setPassword(MqttConfig.mqttPassword.toCharArray()); | ||
| 59 | + mqttclient.connect(options); | ||
| 60 | + return true; | ||
| 61 | + } catch (MqttException e) { | ||
| 62 | + log.error("-----------mqtt连接服务器失败--------------------",e); | ||
| 63 | + } | ||
| 64 | + return false; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + public void start(ParseDataFactory factory) throws MqttException{ | ||
| 68 | + this.parseDataFactory = factory; | ||
| 69 | + log.info("-----------开始启动mqtt监听服务--------------------"); | ||
| 70 | + init(); | ||
| 71 | + connect(); | ||
| 72 | + log.info("-----------mqtt连接服务器成功--------------------"); | ||
| 73 | + | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + @PreDestroy | ||
| 77 | + public void stop() throws MqttException { | ||
| 78 | + if(null != mqttclient) | ||
| 79 | + { | ||
| 80 | + log.info("退出mqtt服务"); | ||
| 81 | + mqttclient.unsubscribe(MqttConfig.subTopic.toArray(new String[MqttConfig.subTopic.size()])); | ||
| 82 | + mqttclient.disconnect(); | ||
| 83 | + mqttclient.close(); | ||
| 84 | + } | ||
| 85 | + instance = null; | ||
| 86 | + } | ||
| 87 | + public void publish(String topic, String messageStr) throws MqttException { | ||
| 88 | + MqttMessage message = new MqttMessage(); | ||
| 89 | + message.setPayload(messageStr.getBytes()); | ||
| 90 | + mqttclient.publish(topic,message); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + public Set<String> subscribe() | ||
| 94 | + { | ||
| 95 | + try { | ||
| 96 | + mqttclient.subscribe(MqttConfig.subTopic.toArray(new String[MqttConfig.subTopic.size()])); | ||
| 97 | + } catch (MqttException e) { | ||
| 98 | + e.printStackTrace(); | ||
| 99 | + } | ||
| 100 | + return MqttConfig.subTopic; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + public boolean isConnect() | ||
| 104 | + { | ||
| 105 | + return mqttclient != null && mqttclient.isConnected(); | ||
| 106 | + } | ||
| 107 | +} |
| @@ -7,6 +7,10 @@ import com.ruoyi.common.utils.StringUtils; | @@ -7,6 +7,10 @@ import com.ruoyi.common.utils.StringUtils; | ||
| 7 | import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.specs.*; | 7 | import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.specs.*; |
| 8 | import com.zhonglai.luhui.device.domain.IotThingsModel; | 8 | import com.zhonglai.luhui.device.domain.IotThingsModel; |
| 9 | import lombok.Data; | 9 | import lombok.Data; |
| 10 | +import org.slf4j.Logger; | ||
| 11 | +import org.slf4j.LoggerFactory; | ||
| 12 | + | ||
| 13 | +import java.util.function.BiConsumer; | ||
| 10 | 14 | ||
| 11 | /** | 15 | /** |
| 12 | * 物模型工厂的公用方法 | 16 | * 物模型工厂的公用方法 |
| @@ -15,6 +19,8 @@ import lombok.Data; | @@ -15,6 +19,8 @@ import lombok.Data; | ||
| 15 | @Data | 19 | @Data |
| 16 | public abstract class ThingsModelItemBase<T> implements ThingsModelBase<T> | 20 | public abstract class ThingsModelItemBase<T> implements ThingsModelBase<T> |
| 17 | { | 21 | { |
| 22 | + private static final Logger log = LoggerFactory.getLogger(ThingsModelItemBase.class); | ||
| 23 | + | ||
| 18 | /** | 24 | /** |
| 19 | * 下位机端数据转换物模型 | 25 | * 下位机端数据转换物模型 |
| 20 | * @param thingsModelDataTypeEnum | 26 | * @param thingsModelDataTypeEnum |
| @@ -24,14 +30,7 @@ public abstract class ThingsModelItemBase<T> implements ThingsModelBase<T> | @@ -24,14 +30,7 @@ public abstract class ThingsModelItemBase<T> implements ThingsModelBase<T> | ||
| 24 | */ | 30 | */ |
| 25 | public static ThingsModelItemBase newhingsModel(ThingsModelDataTypeEnum thingsModelDataTypeEnum,IotThingsModel thingsModel,JsonElement jsonElement) | 31 | public static ThingsModelItemBase newhingsModel(ThingsModelDataTypeEnum thingsModelDataTypeEnum,IotThingsModel thingsModel,JsonElement jsonElement) |
| 26 | { | 32 | { |
| 27 | - if (!jsonElement.isJsonNull()) | ||
| 28 | - { | ||
| 29 | - ThingsModelItemBase thingsModelItemBase = createThingsModelItemBase(thingsModelDataTypeEnum,thingsModel,jsonElement); | ||
| 30 | - thingsModelItemBase.conversionThingsModel(thingsModel); | ||
| 31 | - thingsModelItemBase.setSaveView(jsonElement.getAsString()); | ||
| 32 | - return thingsModelItemBase; | ||
| 33 | - } | ||
| 34 | - return null; | 33 | + return safeNewhingsModel(thingsModelDataTypeEnum, thingsModel, jsonElement, ThingsModelItemBase::setSaveView); |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | /** | 36 | /** |
| @@ -43,12 +42,31 @@ public abstract class ThingsModelItemBase<T> implements ThingsModelBase<T> | @@ -43,12 +42,31 @@ public abstract class ThingsModelItemBase<T> implements ThingsModelBase<T> | ||
| 43 | */ | 42 | */ |
| 44 | public static ThingsModelItemBase newhingsModelReverse(ThingsModelDataTypeEnum thingsModelDataTypeEnum,IotThingsModel thingsModel,JsonElement jsonElement) | 43 | public static ThingsModelItemBase newhingsModelReverse(ThingsModelDataTypeEnum thingsModelDataTypeEnum,IotThingsModel thingsModel,JsonElement jsonElement) |
| 45 | { | 44 | { |
| 46 | - if (!jsonElement.isJsonNull()) | ||
| 47 | - { | ||
| 48 | - ThingsModelItemBase thingsModelItemBase = createThingsModelItemBase(thingsModelDataTypeEnum,thingsModel,jsonElement); | ||
| 49 | - thingsModelItemBase.conversionThingsModel(thingsModel); | ||
| 50 | - thingsModelItemBase.reverse(jsonElement.getAsString()); | ||
| 51 | - return thingsModelItemBase; | 45 | + return safeNewhingsModel(thingsModelDataTypeEnum, thingsModel, jsonElement, ThingsModelItemBase::reverse); |
| 46 | + } | ||
| 47 | + | ||
| 48 | + private static ThingsModelItemBase safeNewhingsModel( | ||
| 49 | + ThingsModelDataTypeEnum type, | ||
| 50 | + IotThingsModel model, | ||
| 51 | + JsonElement element, | ||
| 52 | + BiConsumer<ThingsModelItemBase, String> consumer) { | ||
| 53 | + | ||
| 54 | + if (element != null && !element.isJsonNull()) { | ||
| 55 | + String rawValue = element.getAsString(); | ||
| 56 | + ThingsModelItemBase item = createThingsModelItemBase(type, model, element); | ||
| 57 | + item.conversionThingsModel(model); | ||
| 58 | + | ||
| 59 | + if (rawValue != null && !rawValue.trim().isEmpty()) { | ||
| 60 | + try { | ||
| 61 | + consumer.accept(item, rawValue); | ||
| 62 | + } catch (Exception e) { | ||
| 63 | + log.error("字段 [{}] 处理失败,value={}", model.getModel_name(), rawValue, e); | ||
| 64 | + consumer.accept(item, "0"); // 默认值 | ||
| 65 | + } | ||
| 66 | + } else { | ||
| 67 | + consumer.accept(item, "0"); | ||
| 68 | + } | ||
| 69 | + return item; | ||
| 52 | } | 70 | } |
| 53 | return null; | 71 | return null; |
| 54 | } | 72 | } |
| @@ -20,6 +20,7 @@ | @@ -20,6 +20,7 @@ | ||
| 20 | <module>lh-jar-order-service</module> | 20 | <module>lh-jar-order-service</module> |
| 21 | <module>lh-jar-plugins-init</module> | 21 | <module>lh-jar-plugins-init</module> |
| 22 | <module>lh-jar-ssh-proxy</module> | 22 | <module>lh-jar-ssh-proxy</module> |
| 23 | + <module>lh-device-mqtt-terminal-jar</module> | ||
| 23 | </modules> | 24 | </modules> |
| 24 | 25 | ||
| 25 | <properties> | 26 | <properties> |
| @@ -8,6 +8,7 @@ import com.ruoyi.common.core.domain.Message; | @@ -8,6 +8,7 @@ import com.ruoyi.common.core.domain.Message; | ||
| 8 | import com.ruoyi.common.enums.BusinessType; | 8 | import com.ruoyi.common.enums.BusinessType; |
| 9 | import com.ruoyi.common.utils.GsonConstructor; | 9 | import com.ruoyi.common.utils.GsonConstructor; |
| 10 | import com.zhonglai.luhui.action.BaseController; | 10 | import com.zhonglai.luhui.action.BaseController; |
| 11 | +import com.zhonglai.luhui.api.service.RocketMqSendService; | ||
| 11 | import com.zhonglai.luhui.device.domain.IotDevice; | 12 | import com.zhonglai.luhui.device.domain.IotDevice; |
| 12 | import com.zhonglai.luhui.device.dto.CommandType; | 13 | import com.zhonglai.luhui.device.dto.CommandType; |
| 13 | import com.zhonglai.luhui.device.dto.DeviceCommand; | 14 | import com.zhonglai.luhui.device.dto.DeviceCommand; |
| @@ -32,11 +33,9 @@ import java.io.IOException; | @@ -32,11 +33,9 @@ import java.io.IOException; | ||
| 32 | @RestController | 33 | @RestController |
| 33 | @RequestMapping("/iot/controlDevice") | 34 | @RequestMapping("/iot/controlDevice") |
| 34 | public class ControlDeviceConreoller extends BaseController { | 35 | public class ControlDeviceConreoller extends BaseController { |
| 35 | - @Autowired | ||
| 36 | - private RocketMqService rocketMqService; | ||
| 37 | 36 | ||
| 38 | @Autowired | 37 | @Autowired |
| 39 | - private IIotDeviceService iotDeviceService; | 38 | + private RocketMqSendService rocketMqSendService; |
| 40 | @ApiOperation(value = "写指令",notes = "body参数描述:\r\n" + | 39 | @ApiOperation(value = "写指令",notes = "body参数描述:\r\n" + |
| 41 | "{\n" + | 40 | "{\n" + |
| 42 | " \"0\":{\n" + | 41 | " \"0\":{\n" + |
| @@ -71,7 +70,7 @@ public class ControlDeviceConreoller extends BaseController { | @@ -71,7 +70,7 @@ public class ControlDeviceConreoller extends BaseController { | ||
| 71 | deviceCommand.setCommandType(CommandType.write); | 70 | deviceCommand.setCommandType(CommandType.write); |
| 72 | deviceCommand.setData(GsonConstructor.get().fromJson(body,JsonObject.class)); | 71 | deviceCommand.setData(GsonConstructor.get().fromJson(body,JsonObject.class)); |
| 73 | 72 | ||
| 74 | - return deviceControl(deviceCommand); | 73 | + return rocketMqSendService.deviceControl(deviceCommand); |
| 75 | } | 74 | } |
| 76 | 75 | ||
| 77 | @ApiOperation(value = "读指令",notes = "body参数描述:\r\n" + | 76 | @ApiOperation(value = "读指令",notes = "body参数描述:\r\n" + |
| @@ -108,7 +107,7 @@ public class ControlDeviceConreoller extends BaseController { | @@ -108,7 +107,7 @@ public class ControlDeviceConreoller extends BaseController { | ||
| 108 | deviceCommand.setDeviceId(deviceId); | 107 | deviceCommand.setDeviceId(deviceId); |
| 109 | deviceCommand.setCommandType(CommandType.write); | 108 | deviceCommand.setCommandType(CommandType.write); |
| 110 | deviceCommand.setData(GsonConstructor.get().fromJson(body,JsonObject.class)); | 109 | deviceCommand.setData(GsonConstructor.get().fromJson(body,JsonObject.class)); |
| 111 | - return deviceControl(deviceCommand); | 110 | + return rocketMqSendService.deviceControl(deviceCommand); |
| 112 | } | 111 | } |
| 113 | 112 | ||
| 114 | @ApiOperation("更新缓存模型") | 113 | @ApiOperation("更新缓存模型") |
| @@ -121,7 +120,7 @@ public class ControlDeviceConreoller extends BaseController { | @@ -121,7 +120,7 @@ public class ControlDeviceConreoller extends BaseController { | ||
| 121 | JsonObject jsonObject = new JsonObject(); | 120 | JsonObject jsonObject = new JsonObject(); |
| 122 | jsonObject.addProperty("product_id",product_id); | 121 | jsonObject.addProperty("product_id",product_id); |
| 123 | deviceCommand.setData(jsonObject); | 122 | deviceCommand.setData(jsonObject); |
| 124 | - return sysControl(deviceCommand); | 123 | + return rocketMqSendService.sysControl(deviceCommand); |
| 125 | } | 124 | } |
| 126 | 125 | ||
| 127 | @ApiOperation("更新缓存翻译模型") | 126 | @ApiOperation("更新缓存翻译模型") |
| @@ -134,7 +133,7 @@ public class ControlDeviceConreoller extends BaseController { | @@ -134,7 +133,7 @@ public class ControlDeviceConreoller extends BaseController { | ||
| 134 | JsonObject jsonObject = new JsonObject(); | 133 | JsonObject jsonObject = new JsonObject(); |
| 135 | jsonObject.addProperty("product_id",product_id); | 134 | jsonObject.addProperty("product_id",product_id); |
| 136 | deviceCommand.setData(jsonObject); | 135 | deviceCommand.setData(jsonObject); |
| 137 | - return sysControl(deviceCommand); | 136 | + return rocketMqSendService.sysControl(deviceCommand); |
| 138 | } | 137 | } |
| 139 | 138 | ||
| 140 | @ApiOperation("清除网关缓存") | 139 | @ApiOperation("清除网关缓存") |
| @@ -145,7 +144,7 @@ public class ControlDeviceConreoller extends BaseController { | @@ -145,7 +144,7 @@ public class ControlDeviceConreoller extends BaseController { | ||
| 145 | DeviceCommand deviceCommand = new DeviceCommand(); | 144 | DeviceCommand deviceCommand = new DeviceCommand(); |
| 146 | deviceCommand.setDeviceId(deviceId); | 145 | deviceCommand.setDeviceId(deviceId); |
| 147 | deviceCommand.setCommandType(CommandType.cleanDeviceHost); | 146 | deviceCommand.setCommandType(CommandType.cleanDeviceHost); |
| 148 | - return sysControl(deviceCommand); | 147 | + return rocketMqSendService.sysControl(deviceCommand); |
| 149 | } | 148 | } |
| 150 | 149 | ||
| 151 | @ApiOperation("更新终端缓存") | 150 | @ApiOperation("更新终端缓存") |
| @@ -162,7 +161,7 @@ public class ControlDeviceConreoller extends BaseController { | @@ -162,7 +161,7 @@ public class ControlDeviceConreoller extends BaseController { | ||
| 162 | JsonObject jsonObject = new JsonObject(); | 161 | JsonObject jsonObject = new JsonObject(); |
| 163 | jsonObject.addProperty("sensor_number",sensor_number); | 162 | jsonObject.addProperty("sensor_number",sensor_number); |
| 164 | deviceCommand.setData(jsonObject); | 163 | deviceCommand.setData(jsonObject); |
| 165 | - return sysControl(deviceCommand); | 164 | + return rocketMqSendService.sysControl(deviceCommand); |
| 166 | } | 165 | } |
| 167 | 166 | ||
| 168 | @ApiOperation("添加订阅") | 167 | @ApiOperation("添加订阅") |
| @@ -179,7 +178,7 @@ public class ControlDeviceConreoller extends BaseController { | @@ -179,7 +178,7 @@ public class ControlDeviceConreoller extends BaseController { | ||
| 179 | jsonObject.addProperty("product_ids",product_ids); | 178 | jsonObject.addProperty("product_ids",product_ids); |
| 180 | jsonObject.addProperty("ip",ip); | 179 | jsonObject.addProperty("ip",ip); |
| 181 | deviceCommand.setData(jsonObject); | 180 | deviceCommand.setData(jsonObject); |
| 182 | - return sysControl(deviceCommand); | 181 | + return rocketMqSendService.sysControl(deviceCommand); |
| 183 | } | 182 | } |
| 184 | 183 | ||
| 185 | @ApiOperation(value = "通知",notes = "body参数描述:\r\n" + | 184 | @ApiOperation(value = "通知",notes = "body参数描述:\r\n" + |
| @@ -202,32 +201,8 @@ public class ControlDeviceConreoller extends BaseController { | @@ -202,32 +201,8 @@ public class ControlDeviceConreoller extends BaseController { | ||
| 202 | deviceCommand.setDeviceId(deviceId); | 201 | deviceCommand.setDeviceId(deviceId); |
| 203 | deviceCommand.setCommandType(CommandType.notice); | 202 | deviceCommand.setCommandType(CommandType.notice); |
| 204 | deviceCommand.setData(GsonConstructor.get().fromJson(body,JsonObject.class)); | 203 | deviceCommand.setData(GsonConstructor.get().fromJson(body,JsonObject.class)); |
| 205 | - return deviceControl(deviceCommand); | 204 | + return rocketMqSendService.deviceControl(deviceCommand); |
| 206 | } | 205 | } |
| 207 | 206 | ||
| 208 | - private AjaxResult deviceControl(DeviceCommand deviceCommand) | ||
| 209 | - { | ||
| 210 | - IotDevice iotDevice = iotDeviceService.selectIotDeviceByClient_id(deviceCommand.getDeviceId()); | ||
| 211 | - if(null == iotDevice || iotDevice.getStatus()!=3) | ||
| 212 | - { | ||
| 213 | - return AjaxResult.error("设备不存在或者不在线"); | ||
| 214 | - } | ||
| 215 | 207 | ||
| 216 | - Message message = rocketMqService.send("deviceCommandListen",GsonConstructor.get().toJson(deviceCommand).getBytes(),iotDevice.getOperation_token()); | ||
| 217 | - if(message.getCode()==1) | ||
| 218 | - { | ||
| 219 | - return new AjaxResult(HttpStatus.SUCCESS, message.getMessage(), message.getData()); | ||
| 220 | - } | ||
| 221 | - return new AjaxResult(HttpStatus.ERROR, message.getMessage(), message.getData()); | ||
| 222 | - } | ||
| 223 | - | ||
| 224 | - private AjaxResult sysControl(DeviceCommand deviceCommand) | ||
| 225 | - { | ||
| 226 | - Message message = rocketMqService.send("deviceCommandListen",GsonConstructor.get().toJson(deviceCommand).getBytes(),"SysCommand"); | ||
| 227 | - if(message.getCode()==1) | ||
| 228 | - { | ||
| 229 | - return new AjaxResult(HttpStatus.SUCCESS, message.getMessage(), message.getData()); | ||
| 230 | - } | ||
| 231 | - return new AjaxResult(HttpStatus.ERROR, message.getMessage(), message.getData()); | ||
| 232 | - } | ||
| 233 | } | 208 | } |
lh-modules/lh-api/src/main/java/com/zhonglai/luhui/api/controller/iot/ControlGkjController.java
0 → 100644
| 1 | +package com.zhonglai.luhui.api.controller.iot; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.google.gson.JsonObject; | ||
| 5 | +import com.ruoyi.common.core.domain.AjaxResult; | ||
| 6 | +import com.ruoyi.common.utils.GsonConstructor; | ||
| 7 | +import com.zhonglai.luhui.action.BaseController; | ||
| 8 | +import com.zhonglai.luhui.api.controller.iot.dto.HostCommand; | ||
| 9 | +import com.zhonglai.luhui.api.controller.iot.dto.HostCommandFunction; | ||
| 10 | +import com.zhonglai.luhui.api.controller.iot.dto.camera.StartStream; | ||
| 11 | +import com.zhonglai.luhui.api.controller.iot.dto.camera.StartStreamBySerial; | ||
| 12 | +import com.zhonglai.luhui.api.controller.iot.dto.camera.StopStream; | ||
| 13 | +import com.zhonglai.luhui.api.service.RocketMqSendService; | ||
| 14 | +import com.zhonglai.luhui.device.dto.CommandType; | ||
| 15 | +import com.zhonglai.luhui.device.dto.DeviceCommand; | ||
| 16 | +import io.swagger.annotations.Api; | ||
| 17 | +import io.swagger.annotations.ApiImplicitParam; | ||
| 18 | +import io.swagger.annotations.ApiImplicitParams; | ||
| 19 | +import io.swagger.annotations.ApiOperation; | ||
| 20 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 21 | +import org.springframework.web.bind.annotation.*; | ||
| 22 | + | ||
| 23 | +import java.io.IOException; | ||
| 24 | + | ||
| 25 | +@Api(tags = "工控机控制器") | ||
| 26 | +@RestController | ||
| 27 | +@RequestMapping("/iot/controlDevice") | ||
| 28 | +public class ControlGkjController extends BaseController { | ||
| 29 | + | ||
| 30 | + @Autowired | ||
| 31 | + private RocketMqSendService rocketMqSendService; | ||
| 32 | + | ||
| 33 | + @ApiOperation(value = "ls") | ||
| 34 | + @ApiImplicitParams({ | ||
| 35 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 36 | + }) | ||
| 37 | + @GetMapping("/ls/{deviceId}") | ||
| 38 | + public AjaxResult write(@PathVariable String deviceId) throws IOException { | ||
| 39 | + HostCommand hostCommand = new HostCommand(); | ||
| 40 | + hostCommand.setFunction("ls"); | ||
| 41 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 42 | + deviceCommand.setDeviceId(deviceId); | ||
| 43 | + deviceCommand.setCommandType(CommandType.host); | ||
| 44 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 45 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @ApiOperation(value = "cd") | ||
| 49 | + @ApiImplicitParams({ | ||
| 50 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 51 | + @ApiImplicitParam(name = "path", value = "路径", required = true, dataType = "String", paramType = "path"), | ||
| 52 | + }) | ||
| 53 | + @GetMapping("/cd/{deviceId}") | ||
| 54 | + public AjaxResult cd(@PathVariable String deviceId,String path) { | ||
| 55 | + HostCommand hostCommand = new HostCommand(); | ||
| 56 | + hostCommand.setFunction("cd"); | ||
| 57 | + JSONObject data = new JSONObject(); | ||
| 58 | + data.put("path", path); | ||
| 59 | + hostCommand.setData(data); | ||
| 60 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 61 | + deviceCommand.setDeviceId(deviceId); | ||
| 62 | + deviceCommand.setCommandType(CommandType.host); | ||
| 63 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 64 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + @ApiOperation(value = "mkdir") | ||
| 68 | + @ApiImplicitParams({ | ||
| 69 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 70 | + @ApiImplicitParam(name = "name", value = "名称", required = true, dataType = "String", paramType = "path"), | ||
| 71 | + }) | ||
| 72 | + @GetMapping("/mkdir/{deviceId}") | ||
| 73 | + public AjaxResult mkdir(@PathVariable String deviceId,String name) { | ||
| 74 | + HostCommand hostCommand = new HostCommand(); | ||
| 75 | + hostCommand.setFunction("mkdir"); | ||
| 76 | + JSONObject data = new JSONObject(); | ||
| 77 | + data.put("name", name); | ||
| 78 | + hostCommand.setData(data); | ||
| 79 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 80 | + deviceCommand.setDeviceId(deviceId); | ||
| 81 | + deviceCommand.setCommandType(CommandType.host); | ||
| 82 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 83 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + @ApiOperation(value = "rm") | ||
| 87 | + @ApiImplicitParams({ | ||
| 88 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 89 | + @ApiImplicitParam(name = "name", value = "名称", required = true, dataType = "String", paramType = "path"), | ||
| 90 | + }) | ||
| 91 | + @GetMapping("/rm/{deviceId}") | ||
| 92 | + public AjaxResult rm(@PathVariable String deviceId,String name) { | ||
| 93 | + HostCommand hostCommand = new HostCommand(); | ||
| 94 | + hostCommand.setFunction("rm"); | ||
| 95 | + JSONObject data = new JSONObject(); | ||
| 96 | + data.put("name", name); | ||
| 97 | + hostCommand.setData(data); | ||
| 98 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 99 | + deviceCommand.setDeviceId(deviceId); | ||
| 100 | + deviceCommand.setCommandType(CommandType.host); | ||
| 101 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 102 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + @ApiOperation(value = "copy") | ||
| 106 | + @ApiImplicitParams({ | ||
| 107 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 108 | + @ApiImplicitParam(name = "source", value = "源", required = true, dataType = "String", paramType = "path"), | ||
| 109 | + @ApiImplicitParam(name = "target", value = "目标", required = true, dataType = "String", paramType = "path"), | ||
| 110 | + }) | ||
| 111 | + @GetMapping("/copy/{deviceId}") | ||
| 112 | + public AjaxResult copy(@PathVariable String deviceId,String source,String target) { | ||
| 113 | + HostCommand hostCommand = new HostCommand(); | ||
| 114 | + hostCommand.setFunction("copy"); | ||
| 115 | + JSONObject data = new JSONObject(); | ||
| 116 | + data.put("source", source); | ||
| 117 | + data.put("target", target); | ||
| 118 | + hostCommand.setData(data); | ||
| 119 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 120 | + deviceCommand.setDeviceId(deviceId); | ||
| 121 | + deviceCommand.setCommandType(CommandType.host); | ||
| 122 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 123 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + @ApiOperation(value = "mk") | ||
| 127 | + @ApiImplicitParams({ | ||
| 128 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 129 | + @ApiImplicitParam(name = "name", value = "名称", required = true, dataType = "String", paramType = "path"), | ||
| 130 | + }) | ||
| 131 | + @GetMapping("/mk/{deviceId}") | ||
| 132 | + public AjaxResult mk(@PathVariable String deviceId,String name) { | ||
| 133 | + HostCommand hostCommand = new HostCommand(); | ||
| 134 | + hostCommand.setFunction("mk"); | ||
| 135 | + JSONObject data = new JSONObject(); | ||
| 136 | + data.put("name", name); | ||
| 137 | + hostCommand.setData(data); | ||
| 138 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 139 | + deviceCommand.setDeviceId(deviceId); | ||
| 140 | + deviceCommand.setCommandType(CommandType.host); | ||
| 141 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 142 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + @ApiOperation(value = "download") | ||
| 146 | + @ApiImplicitParams({ | ||
| 147 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 148 | + @ApiImplicitParam(name = "url", value = "url", required = true, dataType = "String", paramType = "path"), | ||
| 149 | + @ApiImplicitParam(name = "name", value = "名称", required = true, dataType = "String", paramType = "path"), | ||
| 150 | + }) | ||
| 151 | + @GetMapping("/download/{deviceId}") | ||
| 152 | + public AjaxResult download(@PathVariable String deviceId,String url,String name) { | ||
| 153 | + HostCommand hostCommand = new HostCommand(); | ||
| 154 | + hostCommand.setFunction("download"); | ||
| 155 | + JSONObject data = new JSONObject(); | ||
| 156 | + data.put("name", name); | ||
| 157 | + data.put("url", url); | ||
| 158 | + hostCommand.setData(data); | ||
| 159 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 160 | + deviceCommand.setDeviceId(deviceId); | ||
| 161 | + deviceCommand.setCommandType(CommandType.host); | ||
| 162 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 163 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + @ApiOperation(value = "upload") | ||
| 167 | + @ApiImplicitParams({ | ||
| 168 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 169 | + @ApiImplicitParam(name = "url", value = "url", required = true, dataType = "String", paramType = "path"), | ||
| 170 | + @ApiImplicitParam(name = "name", value = "名称", required = true, dataType = "String", paramType = "path"), | ||
| 171 | + }) | ||
| 172 | + @GetMapping("/upload/{deviceId}") | ||
| 173 | + public AjaxResult upload(@PathVariable String deviceId,String url,String name) { | ||
| 174 | + HostCommand hostCommand = new HostCommand(); | ||
| 175 | + hostCommand.setFunction("upload"); | ||
| 176 | + JSONObject data = new JSONObject(); | ||
| 177 | + data.put("name", name); | ||
| 178 | + data.put("url", url); | ||
| 179 | + hostCommand.setData(data); | ||
| 180 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 181 | + deviceCommand.setDeviceId(deviceId); | ||
| 182 | + deviceCommand.setCommandType(CommandType.host); | ||
| 183 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 184 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + @ApiOperation(value = "摄像头拉流") | ||
| 188 | + @ApiImplicitParams({ | ||
| 189 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 190 | + }) | ||
| 191 | + @GetMapping("/cameraStartStream/{deviceId}") | ||
| 192 | + public AjaxResult camera(@PathVariable String deviceId, StartStream startStream) { | ||
| 193 | + HostCommand hostCommand = new HostCommand(); | ||
| 194 | + hostCommand.setFunction("camera"); | ||
| 195 | + JSONObject data = new JSONObject(); | ||
| 196 | + data.put("commd", "startStream"); | ||
| 197 | + data.put("param", GsonConstructor.get().toJson(startStream)); | ||
| 198 | + hostCommand.setData(data); | ||
| 199 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 200 | + deviceCommand.setDeviceId(deviceId); | ||
| 201 | + deviceCommand.setCommandType(CommandType.host); | ||
| 202 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 203 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + @ApiOperation(value = "摄像头根据序列号拉流") | ||
| 207 | + @ApiImplicitParams({ | ||
| 208 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 209 | + }) | ||
| 210 | + @GetMapping("/cameraStartStreamBySerial/{deviceId}") | ||
| 211 | + public AjaxResult cameraStartStreamBySerial(@PathVariable String deviceId, StartStreamBySerial startStreamBySerial) { | ||
| 212 | + HostCommand hostCommand = new HostCommand(); | ||
| 213 | + hostCommand.setFunction("camera"); | ||
| 214 | + JSONObject data = new JSONObject(); | ||
| 215 | + data.put("commd", "startStreamBySerial"); | ||
| 216 | + data.put("param", GsonConstructor.get().toJson(startStreamBySerial)); | ||
| 217 | + hostCommand.setData(data); | ||
| 218 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 219 | + deviceCommand.setDeviceId(deviceId); | ||
| 220 | + deviceCommand.setCommandType(CommandType.host); | ||
| 221 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 222 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + @ApiOperation(value = "摄像头关流") | ||
| 226 | + @ApiImplicitParams({ | ||
| 227 | + @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataType = "String", paramType = "path"), | ||
| 228 | + }) | ||
| 229 | + @GetMapping("/cameraStopStream/{deviceId}") | ||
| 230 | + public AjaxResult cameraStopStream(@PathVariable String deviceId, StopStream stopStream) { | ||
| 231 | + HostCommand hostCommand = new HostCommand(); | ||
| 232 | + hostCommand.setFunction("camera"); | ||
| 233 | + JSONObject data = new JSONObject(); | ||
| 234 | + data.put("commd", "stopStream"); | ||
| 235 | + data.put("param", GsonConstructor.get().toJson(stopStream)); | ||
| 236 | + hostCommand.setData(data); | ||
| 237 | + DeviceCommand deviceCommand = new DeviceCommand(); | ||
| 238 | + deviceCommand.setDeviceId(deviceId); | ||
| 239 | + deviceCommand.setCommandType(CommandType.host); | ||
| 240 | + deviceCommand.setData(GsonConstructor.get().fromJson(GsonConstructor.get().toJson(hostCommand), JsonObject.class)); | ||
| 241 | + return rocketMqSendService.deviceControl(deviceCommand); | ||
| 242 | + } | ||
| 243 | +} |
lh-modules/lh-api/src/main/java/com/zhonglai/luhui/api/controller/iot/dto/HostCommand.java
0 → 100644
| 1 | +package com.zhonglai.luhui.api.controller.iot.dto; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | + | ||
| 5 | +public class HostCommand { | ||
| 6 | + private String function; | ||
| 7 | + private JSONObject data; | ||
| 8 | + | ||
| 9 | + public String getFunction() { | ||
| 10 | + return function; | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + public void setFunction(String function) { | ||
| 14 | + this.function = function; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + public JSONObject getData() { | ||
| 18 | + return data; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + public void setData(JSONObject data) { | ||
| 22 | + this.data = data; | ||
| 23 | + } | ||
| 24 | +} |
lh-modules/lh-api/src/main/java/com/zhonglai/luhui/api/controller/iot/dto/camera/StartStream.java
0 → 100644
| 1 | +package com.zhonglai.luhui.api.controller.iot.dto.camera; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | + | ||
| 6 | +@ApiModel("摄像头启动指令") | ||
| 7 | +public class StartStream { | ||
| 8 | + @ApiModelProperty("流id,推荐为摄像头的序列号") | ||
| 9 | + private String stream; | ||
| 10 | + @ApiModelProperty("摄像头的rtsp地址") | ||
| 11 | + private String rtspUrl; | ||
| 12 | + @ApiModelProperty("无人观看自动关闭流") | ||
| 13 | + private String auto_close; | ||
| 14 | + | ||
| 15 | + public String getStream() { | ||
| 16 | + return stream; | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + public void setStream(String stream) { | ||
| 20 | + this.stream = stream; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + public String getRtspUrl() { | ||
| 24 | + return rtspUrl; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + public void setRtspUrl(String rtspUrl) { | ||
| 28 | + this.rtspUrl = rtspUrl; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + public String getAuto_close() { | ||
| 32 | + return auto_close; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public void setAuto_close(String auto_close) { | ||
| 36 | + this.auto_close = auto_close; | ||
| 37 | + } | ||
| 38 | +} |
| 1 | +package com.zhonglai.luhui.api.controller.iot.dto.camera; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | + | ||
| 6 | +@ApiModel("摄像头根据序列号启动指令") | ||
| 7 | +public class StartStreamBySerial { | ||
| 8 | + @ApiModelProperty("摄像头序列号") | ||
| 9 | + private String deviceSerial; | ||
| 10 | + @ApiModelProperty("无人观看自动关闭流") | ||
| 11 | + private String auto_close; | ||
| 12 | + | ||
| 13 | + public String getDeviceSerial() { | ||
| 14 | + return deviceSerial; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + public void setDeviceSerial(String deviceSerial) { | ||
| 18 | + this.deviceSerial = deviceSerial; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + public String getAuto_close() { | ||
| 22 | + return auto_close; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + public void setAuto_close(String auto_close) { | ||
| 26 | + this.auto_close = auto_close; | ||
| 27 | + } | ||
| 28 | +} |
lh-modules/lh-api/src/main/java/com/zhonglai/luhui/api/controller/iot/dto/camera/StopStream.java
0 → 100644
| 1 | +package com.zhonglai.luhui.api.controller.iot.dto.camera; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | + | ||
| 6 | +@ApiModel("摄像头关闭指令") | ||
| 7 | +public class StopStream { | ||
| 8 | + @ApiModelProperty("流id,推荐为摄像头的序列号") | ||
| 9 | + private String stream; | ||
| 10 | + | ||
| 11 | + public String getStream() { | ||
| 12 | + return stream; | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + public void setStream(String stream) { | ||
| 16 | + this.stream = stream; | ||
| 17 | + } | ||
| 18 | +} |
| 1 | +package com.zhonglai.luhui.api.service; | ||
| 2 | + | ||
| 3 | +import com.ruoyi.common.constant.HttpStatus; | ||
| 4 | +import com.ruoyi.common.core.domain.AjaxResult; | ||
| 5 | +import com.ruoyi.common.core.domain.Message; | ||
| 6 | +import com.ruoyi.common.utils.GsonConstructor; | ||
| 7 | +import com.zhonglai.luhui.device.domain.IotDevice; | ||
| 8 | +import com.zhonglai.luhui.device.dto.DeviceCommand; | ||
| 9 | +import com.zhonglai.luhui.device.service.IIotDeviceService; | ||
| 10 | +import com.zhonglai.luhui.rocketmq.service.RocketMqService; | ||
| 11 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 12 | +import org.springframework.stereotype.Service; | ||
| 13 | + | ||
| 14 | +@Service | ||
| 15 | +public class RocketMqSendService { | ||
| 16 | + | ||
| 17 | + @Autowired | ||
| 18 | + private RocketMqService rocketMqService; | ||
| 19 | + | ||
| 20 | + @Autowired | ||
| 21 | + private IIotDeviceService iotDeviceService; | ||
| 22 | + | ||
| 23 | + public AjaxResult deviceControl(DeviceCommand deviceCommand) | ||
| 24 | + { | ||
| 25 | + IotDevice iotDevice = iotDeviceService.selectIotDeviceByClient_id(deviceCommand.getDeviceId()); | ||
| 26 | + if(null == iotDevice || iotDevice.getStatus()!=3) | ||
| 27 | + { | ||
| 28 | + return AjaxResult.error("设备不存在或者不在线"); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + Message message = rocketMqService.send("deviceCommandListen", GsonConstructor.get().toJson(deviceCommand).getBytes(),iotDevice.getOperation_token()); | ||
| 32 | + if(message.getCode()==1) | ||
| 33 | + { | ||
| 34 | + return new AjaxResult(HttpStatus.SUCCESS, message.getMessage(), message.getData()); | ||
| 35 | + } | ||
| 36 | + return new AjaxResult(HttpStatus.ERROR, message.getMessage(), message.getData()); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + public AjaxResult sysControl(DeviceCommand deviceCommand) | ||
| 40 | + { | ||
| 41 | + Message message = rocketMqService.send("deviceCommandListen",GsonConstructor.get().toJson(deviceCommand).getBytes(),"SysCommand"); | ||
| 42 | + if(message.getCode()==1) | ||
| 43 | + { | ||
| 44 | + return new AjaxResult(HttpStatus.SUCCESS, message.getMessage(), message.getData()); | ||
| 45 | + } | ||
| 46 | + return new AjaxResult(HttpStatus.ERROR, message.getMessage(), message.getData()); | ||
| 47 | + } | ||
| 48 | +} |
| 1 | +## 地址类型与范围 | ||
| 2 | +| 类型 | 地址范围 (1-based) | 数据类型 | 说明 | | ||
| 3 | +| ------------------------ | -------------- | --------------- | ---------------------- | | ||
| 4 | +| Coil (线圈输出) | 00001 – 09999 | BIT | 可读可写的单个位,通常用于开关量输出 | | ||
| 5 | +| Discrete Input (离散输入) | 10001 – 19999 | BIT | 只读的单个位,通常用于开关量输入 | | ||
| 6 | +| Input Register (输入寄存器) | 30001 – 39999 | 16-bit 或 32-bit | 只读寄存器,通常用于传感器输入或模拟量 | | ||
| 7 | +| Holding Register (保持寄存器) | 40001 – 49999 | 16-bit 或 32-bit | 可读可写寄存器,通常用于控制命令或模拟量输出 | | ||
| 8 | + | ||
| 9 | +## 常用功能码(Function Code) | ||
| 10 | +| 功能码 | 功能描述 | 适用地址类型 | | ||
| 11 | +| --------- | ------------------------ | ------------------------------ | | ||
| 12 | +| 01 | Read Coils | Coil (00001–09999) | | ||
| 13 | +| 02 | Read Discrete Inputs | Discrete Input (10001–19999) | | ||
| 14 | +| 03 | Read Holding Registers | Holding Register (40001–49999) | | ||
| 15 | +| 04 | Read Input Registers | Input Register (30001–39999) | | ||
| 16 | +| 05 | Write Single Coil | Coil (00001–09999) | | ||
| 17 | +| 06 | Write Single Register | Holding Register (40001–49999) | | ||
| 18 | +| 15 (0x0F) | Write Multiple Coils | Coil (00001–09999) | | ||
| 19 | +| 16 (0x10) | Write Multiple Registers | Holding Register (40001–49999) | | ||
| 20 | + | ||
| 21 | +## 扩展说明 | ||
| 22 | + 1、BIT 类型地址 | ||
| 23 | + Coil、Discrete Input 都是 1 位 (0/1) | ||
| 24 | + Input Register 和 Holding Register 内部也可按 bit 位访问(x.y 格式,例如 40001.0 表示寄存器 40001 的第 0 位) | ||
| 25 | + | ||
| 26 | + 2、寄存器数据类型映射 | ||
| 27 | + | Modbus 寄存器 | BaseLocator 数据类型示例 | 说明 | | ||
| 28 | + | ----------------------- | ------------------------------------------------- | ------------------------ | | ||
| 29 | + | Holding Register 16-bit | TWO\_BYTE\_INT\_SIGNED / TWO\_BYTE\_INT\_UNSIGNED | 标准 16 位整数 | | ||
| 30 | + | Input Register 16-bit | 同上 | 只读 | | ||
| 31 | + | Holding/Input 32-bit | FOUR\_BYTE\_INT\_SIGNED / FOUR\_BYTE\_FLOAT | 两个连续寄存器组成一个 32-bit 整数或浮点 | | ||
| 32 | + | Holding/Input 64-bit | EIGHT\_BYTE\_INT\_SIGNED / EIGHT\_BYTE\_FLOAT | 四个连续寄存器组成 64-bit | | ||
| 33 | + | Holding/Input bit | holdingRegisterBit / inputRegisterBit | 对寄存器内部的某个位操作 | | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + 3、偏移量计算(0-based) | ||
| 37 | + | 地址类型 | 1-based 地址 | 偏移量公式 | | ||
| 38 | + | ---------------- | ----------- | ------------------------ | | ||
| 39 | + | Coil | 00001–09999 | offset = address - 1 | | ||
| 40 | + | Discrete Input | 10001–19999 | offset = address - 10001 | | ||
| 41 | + | Input Register | 30001–39999 | offset = address - 30001 | | ||
| 42 | + | Holding Register | 40001–49999 | offset = address - 40001 | |
lh-modules/lh-device-modbus-terminal/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 5 | + <modelVersion>4.0.0</modelVersion> | ||
| 6 | + <parent> | ||
| 7 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 8 | + <artifactId>lh-modules</artifactId> | ||
| 9 | + <version>1.0-SNAPSHOT</version> | ||
| 10 | + </parent> | ||
| 11 | + | ||
| 12 | + <artifactId>lh-device-modbus-terminal</artifactId> | ||
| 13 | + | ||
| 14 | + <properties> | ||
| 15 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 16 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 17 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 18 | + </properties> | ||
| 19 | + | ||
| 20 | + <dependencies> | ||
| 21 | + <dependency> | ||
| 22 | + <groupId>com.infiniteautomation</groupId> | ||
| 23 | + <artifactId>modbus4j</artifactId> | ||
| 24 | + </dependency> | ||
| 25 | + | ||
| 26 | + <dependency> | ||
| 27 | + <groupId>com.fasterxml.jackson.core</groupId> | ||
| 28 | + <artifactId>jackson-databind</artifactId> | ||
| 29 | + </dependency> | ||
| 30 | + | ||
| 31 | + <dependency> | ||
| 32 | + <groupId>com.fazecast</groupId> | ||
| 33 | + <artifactId>jSerialComm</artifactId> | ||
| 34 | + </dependency> | ||
| 35 | + | ||
| 36 | + <!-- 阿里JSON解析器 --> | ||
| 37 | + <dependency> | ||
| 38 | + <groupId>com.alibaba</groupId> | ||
| 39 | + <artifactId>fastjson</artifactId> | ||
| 40 | + </dependency> | ||
| 41 | + | ||
| 42 | + <!-- mqtt终端插件 --> | ||
| 43 | + <dependency> | ||
| 44 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 45 | + <artifactId>lh-device-mqtt-terminal-jar</artifactId> | ||
| 46 | + </dependency> | ||
| 47 | + <dependency> | ||
| 48 | + <groupId>cn.hutool</groupId> | ||
| 49 | + <artifactId>hutool-all</artifactId> | ||
| 50 | + </dependency> | ||
| 51 | + | ||
| 52 | + <dependency> | ||
| 53 | + <groupId>org.ini4j</groupId> | ||
| 54 | + <artifactId>ini4j</artifactId> | ||
| 55 | + </dependency> | ||
| 56 | + </dependencies> | ||
| 57 | + | ||
| 58 | + <build> | ||
| 59 | + <finalName>lh-device-modbus-terminal</finalName> | ||
| 60 | + <plugins> | ||
| 61 | + <plugin> | ||
| 62 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 63 | + <artifactId>maven-jar-plugin</artifactId> | ||
| 64 | + <version>2.4</version> | ||
| 65 | + <configuration> | ||
| 66 | + <archive> | ||
| 67 | + <!-- | ||
| 68 | + 生成的jar中,不要包含pom.xml和pom.properties这两个文件 | ||
| 69 | + --> | ||
| 70 | + <addMavenDescriptor>false</addMavenDescriptor> | ||
| 71 | + <manifest> | ||
| 72 | + <!-- | ||
| 73 | + 是否要把第三方jar放到manifest的classpath中 | ||
| 74 | + --> | ||
| 75 | + <addClasspath>true</addClasspath> | ||
| 76 | + | ||
| 77 | + <!-- | ||
| 78 | + 生成的manifest中classpath的前缀,因为要把第三方jar放到lib目录下,所以classpath的前缀是lib/ | ||
| 79 | + --> | ||
| 80 | + <classpathPrefix>lib/</classpathPrefix> | ||
| 81 | + <mainClass>com.zhonglai.luhui.device.modbus.terminal.Main</mainClass> | ||
| 82 | + </manifest> | ||
| 83 | + </archive> | ||
| 84 | + </configuration> | ||
| 85 | + </plugin> | ||
| 86 | + | ||
| 87 | + <!-- The configuration of maven-assembly-plugin --> | ||
| 88 | + <plugin> | ||
| 89 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 90 | + <artifactId>maven-assembly-plugin</artifactId> | ||
| 91 | + <version>2.4</version> | ||
| 92 | + <configuration> | ||
| 93 | + <descriptors> | ||
| 94 | + <descriptor>${project.parent.parent.basedir}/configs/package.xml</descriptor> | ||
| 95 | + </descriptors> | ||
| 96 | + </configuration> | ||
| 97 | + <executions> | ||
| 98 | + <execution> | ||
| 99 | + <id>make-assembly</id> | ||
| 100 | + <phase>package</phase> | ||
| 101 | + <goals> | ||
| 102 | + <goal>single</goal> | ||
| 103 | + </goals> | ||
| 104 | + </execution> | ||
| 105 | + </executions> | ||
| 106 | + </plugin> | ||
| 107 | + </plugins> | ||
| 108 | + </build> | ||
| 109 | +</project> |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.zhonglai.luhui.device.modbus.terminal.config.CameraConfig; | ||
| 5 | +import com.zhonglai.luhui.device.modbus.terminal.config.InitPlcConfig; | ||
| 6 | +import com.zhonglai.luhui.device.modbus.terminal.data.ParseDataService; | ||
| 7 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jWrite; | ||
| 8 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.ModbusMasterMessage; | ||
| 9 | +import com.zhonglai.luhui.device.modbus.terminal.task.CollectPlcDataTask; | ||
| 10 | +import com.zhonglai.luhui.device.modbus.terminal.task.ScheduledThreadPool; | ||
| 11 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.config.MqttConfig; | ||
| 12 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt.MqttService; | ||
| 13 | +import org.apache.commons.logging.Log; | ||
| 14 | +import org.apache.commons.logging.LogFactory; | ||
| 15 | + | ||
| 16 | +public class Main { | ||
| 17 | + private static Log log = LogFactory.getLog(Modbus4jWrite.class); | ||
| 18 | + public static void main(String[] args) throws Exception { | ||
| 19 | + | ||
| 20 | + String configPath = null; | ||
| 21 | + if (args.length!=0) | ||
| 22 | + { | ||
| 23 | + configPath = args[0]; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + //启动mqtt服务 | ||
| 27 | + MqttConfig.init(configPath); // 先加载配置 | ||
| 28 | + MqttService mqttService = MqttService.getInstance(); | ||
| 29 | + mqttService.start(new ParseDataService()); | ||
| 30 | + | ||
| 31 | + //加载modbus配置 | ||
| 32 | + String jsonPath = Main.class.getClassLoader().getResource("configs/plcs.json").getPath();; | ||
| 33 | + if (null != configPath) | ||
| 34 | + { | ||
| 35 | + jsonPath = configPath+"/plcs.json"; | ||
| 36 | + } | ||
| 37 | + InitPlcConfig.initPlcConfigFromFile(jsonPath); | ||
| 38 | + | ||
| 39 | + String camerapath = Main.class.getClassLoader().getResource("configs/camera.properties").getPath();; | ||
| 40 | + if (null != configPath) | ||
| 41 | + { | ||
| 42 | + camerapath = configPath+"/camera.properties"; | ||
| 43 | + } | ||
| 44 | + CameraConfig.init(camerapath); | ||
| 45 | + | ||
| 46 | + CollectPlcDataTask collectPlcDataTask = new CollectPlcDataTask(); | ||
| 47 | + collectPlcDataTask.collect(mqttService); | ||
| 48 | + | ||
| 49 | + // 添加 JVM 关闭钩子,保证优雅退出 | ||
| 50 | + Runtime.getRuntime().addShutdownHook(new Thread(() -> { | ||
| 51 | + log.info("🛑 应用关闭中,正在释放资源..."); | ||
| 52 | + try { | ||
| 53 | + MqttService.getInstance().stop(); | ||
| 54 | + ModbusMasterMessage.closeAll(); | ||
| 55 | + ScheduledThreadPool.stopScheduler(); | ||
| 56 | + log.info("✅ 资源释放完成"); | ||
| 57 | + } catch (Exception e) { | ||
| 58 | + e.printStackTrace(); | ||
| 59 | + } | ||
| 60 | + })); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | +} | ||
| 64 | + |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.camera; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.net.NetUtil; | ||
| 4 | +import cn.hutool.http.HttpUtil; | ||
| 5 | +import com.alibaba.fastjson.JSON; | ||
| 6 | +import com.alibaba.fastjson.JSONObject; | ||
| 7 | +import com.zhonglai.luhui.device.modbus.terminal.config.CameraConfig; | ||
| 8 | +import org.slf4j.Logger; | ||
| 9 | +import org.slf4j.LoggerFactory; | ||
| 10 | + | ||
| 11 | +import java.net.DatagramSocket; | ||
| 12 | +import java.net.InetAddress; | ||
| 13 | + | ||
| 14 | +public class WebRtcService { | ||
| 15 | + private static Logger logger = LoggerFactory.getLogger(WebRtcService.class); | ||
| 16 | + | ||
| 17 | + public static StringBuffer getZlmApi() | ||
| 18 | + { | ||
| 19 | + return new StringBuffer().append("http://").append(CameraConfig.webrtc_host).append("/index/api"); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + public static String getPlayUrl(String ip,String app,String stream) | ||
| 23 | + { | ||
| 24 | + return "https://"+ip+"/index/api/webrtc?app="+app+"&stream="+stream+"&type=play"; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + public static boolean isMediaOnline(String stream,String app,String secret) { | ||
| 28 | + String url = getZlmApi() + "/isMediaOnline?secret="+secret+"&schema=rtsp&vhost=__defaultVhost__&app="+app+"&stream="+stream; | ||
| 29 | + String str = HttpUtil.get(url); | ||
| 30 | + System.out.println(str); | ||
| 31 | + return JSON.parseObject(str).containsKey("online") && JSON.parseObject(str).getBoolean("online"); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.camera.opf; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 摄像头操作指令 | ||
| 7 | + */ | ||
| 8 | +public class CameraOperationInstructions { | ||
| 9 | + private String commd; | ||
| 10 | + private CameraCommdFunction param; | ||
| 11 | + | ||
| 12 | + public String getCommd() { | ||
| 13 | + return commd; | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + public void setCommd(String commd) { | ||
| 17 | + this.commd = commd; | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + public CameraCommdFunction getParam() { | ||
| 21 | + return param; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + public void setParam(CameraCommdFunction param) { | ||
| 25 | + this.param = param; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + public static CameraOperationInstructions createCameraOperationInstructions(JSONObject parameter) | ||
| 29 | + { | ||
| 30 | + CameraOperationInstructions cameraOperationInstructions = new CameraOperationInstructions(); | ||
| 31 | + cameraOperationInstructions.setCommd(parameter.getString("commd")); | ||
| 32 | + String jsonstr = parameter.getString("param"); | ||
| 33 | + switch (cameraOperationInstructions.getCommd()) | ||
| 34 | + { | ||
| 35 | + case "startStream": | ||
| 36 | + cameraOperationInstructions.setParam(JSONObject.parseObject(jsonstr, StartStream.class)); | ||
| 37 | + break; | ||
| 38 | + case "stopStream": | ||
| 39 | + cameraOperationInstructions.setParam(JSONObject.parseObject(jsonstr, StopStream.class)); | ||
| 40 | + break; | ||
| 41 | + case "startStreamBySerial": | ||
| 42 | + cameraOperationInstructions.setParam(JSONObject.parseObject(jsonstr, StartStreamBySerial.class)); | ||
| 43 | + break; | ||
| 44 | + case "getLocalIpAddress": | ||
| 45 | + break; | ||
| 46 | + } | ||
| 47 | + return cameraOperationInstructions; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + public JSONObject execute() | ||
| 51 | + { | ||
| 52 | + return param.execute(); | ||
| 53 | + } | ||
| 54 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.camera.opf; | ||
| 2 | + | ||
| 3 | +import cn.hutool.http.HttpUtil; | ||
| 4 | +import com.alibaba.fastjson.JSON; | ||
| 5 | +import com.alibaba.fastjson.JSONObject; | ||
| 6 | +import com.zhonglai.luhui.device.modbus.terminal.camera.WebRtcService; | ||
| 7 | +import com.zhonglai.luhui.device.modbus.terminal.config.CameraConfig; | ||
| 8 | +import org.slf4j.Logger; | ||
| 9 | +import org.slf4j.LoggerFactory; | ||
| 10 | + | ||
| 11 | +public class StartStream implements CameraCommdFunction { | ||
| 12 | + private static Logger logger = LoggerFactory.getLogger(StartStream.class); | ||
| 13 | + private String stream; | ||
| 14 | + private String rtspUrl; | ||
| 15 | + private String auto_close; | ||
| 16 | + | ||
| 17 | + public String getStream() { | ||
| 18 | + return stream; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + public void setStream(String stream) { | ||
| 22 | + this.stream = stream; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + public String getRtspUrl() { | ||
| 26 | + return rtspUrl; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + public void setRtspUrl(String rtspUrl) { | ||
| 30 | + this.rtspUrl = rtspUrl; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public String getAuto_close() { | ||
| 34 | + return auto_close; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public void setAuto_close(String auto_close) { | ||
| 38 | + this.auto_close = auto_close; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + @Override | ||
| 42 | + public JSONObject execute() { | ||
| 43 | + String[] rtspurls = rtspUrl.split(","); | ||
| 44 | + if(rtspurls.length!=0) | ||
| 45 | + { | ||
| 46 | + String playurls = ""; | ||
| 47 | + String localip = CameraConfig.localIp; | ||
| 48 | + for(String rtspurl : rtspurls) { | ||
| 49 | + String newStream = stream; | ||
| 50 | + String playurl = WebRtcService.getPlayUrl(localip, CameraConfig.webrtc_app, newStream); | ||
| 51 | + boolean oline = WebRtcService.isMediaOnline(newStream, CameraConfig.webrtc_app, CameraConfig.webrtcSecret); | ||
| 52 | + if (!oline) | ||
| 53 | + { | ||
| 54 | + StringBuffer stringBuffer = WebRtcService.getZlmApi(); | ||
| 55 | + stringBuffer.append("/addStreamProxy?"); | ||
| 56 | + stringBuffer.append("secret="); | ||
| 57 | + stringBuffer.append( CameraConfig.webrtcSecret); | ||
| 58 | + stringBuffer.append("&vhost=__defaultVhost__"); | ||
| 59 | + stringBuffer.append("&app="); | ||
| 60 | + stringBuffer.append( CameraConfig.webrtc_app); | ||
| 61 | + stringBuffer.append("&stream="); | ||
| 62 | + stringBuffer.append(newStream); | ||
| 63 | + stringBuffer.append("&url="); | ||
| 64 | + stringBuffer.append(rtspurl); | ||
| 65 | + stringBuffer.append("&enable_auto_close=1"); | ||
| 66 | + stringBuffer.append("&retry_count=-1&rtp_type=0&timeout_sec=10&enable_hls=false&enable_hls_fmp4=false&enable_mp4=false&enable_rtsp=true&enable_rtmp=false&enable_ts=false&enable_fmp4=true&hls_demand=false&rtsp_demand=false&rtmp_demand=false&ts_demand=false&fmp4_demand=false&enable_audio=true&add_mute_audio=true&mp4_max_second=10&mp4_as_player=false&auto_close="+auto_close); | ||
| 67 | + logger.info("添加流的接口请求:"+stringBuffer.toString()); | ||
| 68 | + String str = HttpUtil.get(stringBuffer.toString()); | ||
| 69 | + JSONObject jsonObject = JSON.parseObject(str); | ||
| 70 | + if(jsonObject.containsKey("code") && jsonObject.getInteger("code")==0) | ||
| 71 | + { | ||
| 72 | + jsonObject.put("code",1); | ||
| 73 | + if(!"".equals(playurls)) | ||
| 74 | + { | ||
| 75 | + playurls += ","; | ||
| 76 | + } | ||
| 77 | + playurls+=playurl; | ||
| 78 | + }else { | ||
| 79 | + jsonObject.put("code",0); | ||
| 80 | + System.out.println(str); | ||
| 81 | + return jsonObject; | ||
| 82 | + } | ||
| 83 | + }else{ | ||
| 84 | + if(!"".equals(playurls)) | ||
| 85 | + { | ||
| 86 | + playurls += ","; | ||
| 87 | + } | ||
| 88 | + playurls+=playurl; | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + } | ||
| 92 | + JSONObject jsonObject = new JSONObject(); | ||
| 93 | + jsonObject.put("code",1); | ||
| 94 | + JSONObject data = new JSONObject(); | ||
| 95 | + data.put("playurl",playurls); | ||
| 96 | + data.put("key", CameraConfig.webrtcSecret); | ||
| 97 | + jsonObject.put("data",data); | ||
| 98 | + return jsonObject; | ||
| 99 | + } | ||
| 100 | + JSONObject jsonObject = new JSONObject(); | ||
| 101 | + jsonObject.put("code",0); | ||
| 102 | + jsonObject.put("msg","rtspUrl参数错误"); | ||
| 103 | + return jsonObject; | ||
| 104 | + } | ||
| 105 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.camera.opf; | ||
| 2 | + | ||
| 3 | +import cn.hutool.http.HttpUtil; | ||
| 4 | +import com.alibaba.fastjson.JSON; | ||
| 5 | +import com.alibaba.fastjson.JSONObject; | ||
| 6 | +import com.zhonglai.luhui.device.modbus.terminal.config.CameraConfig; | ||
| 7 | +import org.slf4j.Logger; | ||
| 8 | +import org.slf4j.LoggerFactory; | ||
| 9 | + | ||
| 10 | +public class StartStreamBySerial implements CameraCommdFunction { | ||
| 11 | + private static Logger logger = LoggerFactory.getLogger(StartStream.class); | ||
| 12 | + private String deviceSerial; | ||
| 13 | + private String auto_close; | ||
| 14 | + | ||
| 15 | + public String getDeviceSerial() { | ||
| 16 | + return deviceSerial; | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + public void setDeviceSerial(String deviceSerial) { | ||
| 20 | + this.deviceSerial = deviceSerial; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + public String getAuto_close() { | ||
| 24 | + return auto_close; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + public void setAuto_close(String auto_close) { | ||
| 28 | + this.auto_close = auto_close; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + @Override | ||
| 32 | + public JSONObject execute() { | ||
| 33 | + String str = HttpUtil.get(CameraConfig.yuerleApiUrl+"?deviceSerial="+deviceSerial); | ||
| 34 | + JSONObject jsonObject = JSON.parseObject(str); | ||
| 35 | + if(jsonObject.containsKey("code") && jsonObject.getInteger("code")==1 && null != jsonObject.get("data")) | ||
| 36 | + { | ||
| 37 | + String enable_rtsp = jsonObject.getString("data"); | ||
| 38 | + logger.info("令牌{}获取到设备{}的rtsp地址{}",CameraConfig.webrtcSecret,deviceSerial,enable_rtsp); | ||
| 39 | + StartStream startStream = new StartStream(); | ||
| 40 | + startStream.setRtspUrl(enable_rtsp); | ||
| 41 | + startStream.setAuto_close(auto_close); | ||
| 42 | + startStream.setStream(deviceSerial); | ||
| 43 | + return startStream.execute(); | ||
| 44 | + } | ||
| 45 | + return jsonObject; | ||
| 46 | + } | ||
| 47 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.camera.opf; | ||
| 2 | + | ||
| 3 | +import cn.hutool.http.HttpUtil; | ||
| 4 | +import com.alibaba.fastjson.JSONObject; | ||
| 5 | +import com.zhonglai.luhui.device.modbus.terminal.camera.WebRtcService; | ||
| 6 | +import com.zhonglai.luhui.device.modbus.terminal.config.CameraConfig; | ||
| 7 | + | ||
| 8 | +public class StopStream implements CameraCommdFunction { | ||
| 9 | + private String stream; | ||
| 10 | + | ||
| 11 | + public String getStream() { | ||
| 12 | + return stream; | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + public void setStream(String stream) { | ||
| 16 | + this.stream = stream; | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + @Override | ||
| 20 | + public JSONObject execute() { | ||
| 21 | + String url = WebRtcService.getZlmApi() + "/close_stream?schema=rtsp&vhost=__defaultVhost__&app=" + CameraConfig.webrtc_app + "&stream=" + stream+"&force=1&secret="+ CameraConfig.webrtcSecret; | ||
| 22 | + String str = HttpUtil.get(url); | ||
| 23 | + System.out.println(str); | ||
| 24 | + return JSONObject.parseObject(str); | ||
| 25 | + } | ||
| 26 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.config; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.io.FileUtil; | ||
| 4 | +import cn.hutool.core.net.NetUtil; | ||
| 5 | +import com.zhonglai.luhui.device.modbus.terminal.camera.WebRtcService; | ||
| 6 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.config.MqttConfig; | ||
| 7 | +import org.ini4j.Ini; | ||
| 8 | +import org.slf4j.Logger; | ||
| 9 | +import org.slf4j.LoggerFactory; | ||
| 10 | + | ||
| 11 | +import java.io.*; | ||
| 12 | +import java.net.DatagramSocket; | ||
| 13 | +import java.net.InetAddress; | ||
| 14 | +import java.util.Properties; | ||
| 15 | + | ||
| 16 | +public class CameraConfig { | ||
| 17 | + private static Logger logger = LoggerFactory.getLogger(CameraConfig.class); | ||
| 18 | + public static String yuerleApiUrl; | ||
| 19 | + public static String webrtc_host; | ||
| 20 | + | ||
| 21 | + public static String webrtcSecret; | ||
| 22 | + | ||
| 23 | + public static String webrtc_app; | ||
| 24 | + | ||
| 25 | + public static String localIp; | ||
| 26 | + | ||
| 27 | + public static void init(String configPath ) | ||
| 28 | + { | ||
| 29 | + Properties properties = loadProperties(configPath); | ||
| 30 | + | ||
| 31 | + yuerleApiUrl = properties.getProperty("yuerleApiUrl"); | ||
| 32 | + webrtc_app = properties.getProperty("webrtc_app"); | ||
| 33 | + webrtc_host = properties.getProperty("webrtc_host"); | ||
| 34 | + webrtcSecret = null==properties.getProperty("webrtcSecret")?getWebrtcSecret():properties.getProperty("webrtcSecret"); | ||
| 35 | + String ip = getLocalIp(); | ||
| 36 | + localIp = null == ip?getLocalIpAddress():ip; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + public static String getLocalIp() | ||
| 40 | + { | ||
| 41 | + File file = new File("/app/zlmediakit_config/ip.txt"); | ||
| 42 | + if (file.exists()) | ||
| 43 | + { | ||
| 44 | + String str = FileUtil.readString(file, "utf-8"); | ||
| 45 | + String result = str.trim().replaceAll("[\\r\\n]", ""); | ||
| 46 | + return result; | ||
| 47 | + } | ||
| 48 | + String localIpEnv = System.getenv("localIp"); | ||
| 49 | + if (localIpEnv != null) { | ||
| 50 | + try { | ||
| 51 | + String realIp = InetAddress.getByName(localIpEnv).getHostAddress(); | ||
| 52 | + return realIp; | ||
| 53 | + } catch (Exception e) { | ||
| 54 | + logger.error("宿主机 IP 获取失败",e); | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | + return null; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + public static String getZLMSecret() { | ||
| 61 | + File file = new File("/app/zlmediakit_config/config.ini"); | ||
| 62 | + | ||
| 63 | + if (file.exists()) | ||
| 64 | + { | ||
| 65 | + try { | ||
| 66 | + // 读取 config.ini 文件 | ||
| 67 | + Ini ini = new Ini(file); | ||
| 68 | + // 获取 [api] 段里的 secret 属性 | ||
| 69 | + String secret = ini.get("api", "secret"); | ||
| 70 | + logger.info("zlmediakit配置文件读取成功:"+secret); | ||
| 71 | + return secret; | ||
| 72 | + } catch (IOException e) { | ||
| 73 | + logger.error("zlmediakit配置文件读取失败",e); | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + String[] command = { | ||
| 78 | + "docker", "exec", "zlmediakit", | ||
| 79 | + "grep", "^secret=", "/opt/media/conf/config.ini" | ||
| 80 | + }; | ||
| 81 | + | ||
| 82 | + logger.info("正在通过指令{}获取操作令牌...",command); | ||
| 83 | + ProcessBuilder processBuilder = new ProcessBuilder(command); | ||
| 84 | + processBuilder.redirectErrorStream(true); // 合并错误流和标准输出流 | ||
| 85 | + | ||
| 86 | + try { | ||
| 87 | + Process process = processBuilder.start(); | ||
| 88 | + | ||
| 89 | + try (BufferedReader reader = new BufferedReader( | ||
| 90 | + new InputStreamReader(process.getInputStream()))) { | ||
| 91 | + String line; | ||
| 92 | + while ((line = reader.readLine()) != null) { | ||
| 93 | + logger.info("执行指令返回的数据:{}",line); | ||
| 94 | + if (line.startsWith("secret=")) { | ||
| 95 | + return line.substring("secret=".length()).trim(); | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + int exitCode = process.waitFor(); | ||
| 101 | + if (exitCode != 0) { | ||
| 102 | + logger.info("命令执行失败,退出码:"+exitCode); | ||
| 103 | + } | ||
| 104 | + } catch (Exception e) { | ||
| 105 | + logger.info("拿操作令牌异常,去配置里面拿:",e); | ||
| 106 | + return "keXTvTDSHAxFDpBA0MDAHhxWeVXLQmUq"; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + | ||
| 110 | + return null; // 未找到 secret 或执行失败 | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * 获取webrtc的密钥 | ||
| 115 | + * @return | ||
| 116 | + */ | ||
| 117 | + public static String getWebrtcSecret() { | ||
| 118 | + if(null == webrtcSecret) | ||
| 119 | + { | ||
| 120 | + refreshWebrtcSecret(); | ||
| 121 | + } | ||
| 122 | + return webrtcSecret; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + /** | ||
| 126 | + * 刷新webrtc的密钥 | ||
| 127 | + */ | ||
| 128 | + public static void refreshWebrtcSecret() | ||
| 129 | + { | ||
| 130 | + webrtcSecret = getZLMSecret(); | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + private static Properties loadProperties(String path) | ||
| 134 | + { | ||
| 135 | + Properties properties = new Properties(); | ||
| 136 | + try { | ||
| 137 | + if(null != path && !"".equals(path)) | ||
| 138 | + { | ||
| 139 | + properties.load(new FileReader(path)); | ||
| 140 | + }else{ | ||
| 141 | + properties.load(MqttConfig.class.getClassLoader().getResourceAsStream("configs/camera.properties")); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + } catch (Exception e) { | ||
| 145 | + throw new RuntimeException("加载camera.properties失败,未找到配置文件或内容为空"); | ||
| 146 | + } | ||
| 147 | + return properties; | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + /** | ||
| 151 | + * 获取本机IP | ||
| 152 | + * @return | ||
| 153 | + */ | ||
| 154 | + public static String getLocalIpAddress() { | ||
| 155 | + if(null != localIp) | ||
| 156 | + { | ||
| 157 | + return localIp; | ||
| 158 | + } | ||
| 159 | + try { | ||
| 160 | + // 构造一个连接外部地址的 socket(这里用 Google 的公共 DNS IP) | ||
| 161 | + try (DatagramSocket socket = new DatagramSocket()) { | ||
| 162 | + socket.connect(InetAddress.getByName("8.8.8.8"), 10002); | ||
| 163 | + InetAddress localAddress = socket.getLocalAddress(); | ||
| 164 | + String ip = localAddress.getHostAddress(); | ||
| 165 | + if("0.0.0.0".equals(ip)) | ||
| 166 | + { | ||
| 167 | + return null; | ||
| 168 | + } | ||
| 169 | + return localIp=ip; | ||
| 170 | + } | ||
| 171 | + } catch (Exception e) { | ||
| 172 | + e.printStackTrace(); | ||
| 173 | + return null; | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.config; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.core.type.TypeReference; | ||
| 4 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 5 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.CachPlcConfig; | ||
| 6 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint; | ||
| 7 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcSystem; | ||
| 8 | + | ||
| 9 | +import java.io.File; | ||
| 10 | +import java.io.IOException; | ||
| 11 | +import java.util.HashMap; | ||
| 12 | +import java.util.List; | ||
| 13 | +import java.util.Map; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * plc配置 | ||
| 17 | + */ | ||
| 18 | +public class InitPlcConfig { | ||
| 19 | + private static Map<Integer, CachPlcConfig> plcsConfigMap = new HashMap<>(); | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 从文件初始化plc点位配置 | ||
| 23 | + * @param jsonPath | ||
| 24 | + * @throws IOException | ||
| 25 | + */ | ||
| 26 | + public static void initPlcConfigFromFile(String jsonPath) throws IOException { | ||
| 27 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 28 | + Map<String, List<PlcSystem>> plcsMap = mapper.readValue(new File(jsonPath), | ||
| 29 | + new TypeReference<Map<String, List<PlcSystem>>>() {}); | ||
| 30 | + List<PlcSystem> plcSystems = plcsMap.get("plcs"); | ||
| 31 | + for (PlcSystem plc : plcSystems) | ||
| 32 | + { | ||
| 33 | + CachPlcConfig cachPlcConfig = plcsConfigMap.get(plc.getId()); | ||
| 34 | + if (cachPlcConfig == null) | ||
| 35 | + { | ||
| 36 | + cachPlcConfig = new CachPlcConfig(); | ||
| 37 | + cachPlcConfig.setId(plc.getId()); | ||
| 38 | + cachPlcConfig.setSystemName(plc.getSystemName()); | ||
| 39 | + cachPlcConfig.setProtocolType(plc.getProtocolType()); | ||
| 40 | + cachPlcConfig.setConnectConfig(plc.getConnectConfig()); | ||
| 41 | + cachPlcConfig.setPlcMap(new HashMap()); | ||
| 42 | + | ||
| 43 | + plcsConfigMap.put(plc.getId(), cachPlcConfig); | ||
| 44 | + } | ||
| 45 | + Map<String, PlcPoint> plcMap = cachPlcConfig.getPlcMap(); | ||
| 46 | + if(plcMap.size()==0 && plc.getPoints().size()>0) | ||
| 47 | + { | ||
| 48 | + Map<String, PlcPoint> finalPlcMap = plcMap; | ||
| 49 | + plc.getPoints().forEach(p -> finalPlcMap.put(p.system, p)); | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * 获取指定plc的点位配置 | ||
| 56 | + * @param id 系统编号 | ||
| 57 | + * @return | ||
| 58 | + */ | ||
| 59 | + public static CachPlcConfig getPlcSystems(Integer id) | ||
| 60 | + { | ||
| 61 | + return plcsConfigMap.get(id); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 获取指定plc的指定点位配置 | ||
| 66 | + * @param system 指定点位系统名 | ||
| 67 | + * @param id 系统编号 | ||
| 68 | + * @return | ||
| 69 | + */ | ||
| 70 | + public static PlcPoint getPlcSystem(Integer id,String system) | ||
| 71 | + { | ||
| 72 | + if (plcsConfigMap.containsKey(id) && plcsConfigMap.get(id).getPlcMap().containsKey(system)) return plcsConfigMap.get(id).getPlcMap().get(system); | ||
| 73 | + return null; | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + public static Map<Integer, CachPlcConfig> getPlcsConfigMap() | ||
| 77 | + { | ||
| 78 | + return plcsConfigMap; | ||
| 79 | + } | ||
| 80 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.data; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.ParseDataFactory; | ||
| 4 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.dto.Topic; | ||
| 5 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt.MqttService; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 解析数据服务 | ||
| 9 | + */ | ||
| 10 | +public class ParseDataService implements ParseDataFactory { | ||
| 11 | + @Override | ||
| 12 | + public void parse(Topic topicDto, byte[] payload, MqttService mqttService) { | ||
| 13 | + TopicFactoryAdapter topicFactory = TopicFactoryAdapter.createTopicFactory(topicDto, payload); | ||
| 14 | + if(null != topicFactory) | ||
| 15 | + { | ||
| 16 | + topicFactory.parse(mqttService); | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.data; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.device.modbus.terminal.data.topic.HostTopic; | ||
| 4 | +import com.zhonglai.luhui.device.modbus.terminal.data.topic.PutTopic; | ||
| 5 | +import com.zhonglai.luhui.device.modbus.terminal.data.topic.ReadTopic; | ||
| 6 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.dto.Topic; | ||
| 7 | +import org.slf4j.Logger; | ||
| 8 | +import org.slf4j.LoggerFactory; | ||
| 9 | + | ||
| 10 | +public abstract class TopicFactoryAdapter implements TopicFactory{ | ||
| 11 | + | ||
| 12 | + protected static final Logger logger = LoggerFactory.getLogger(TopicFactoryAdapter.class); | ||
| 13 | + | ||
| 14 | + protected Topic topicDto; | ||
| 15 | + protected byte[] payload; | ||
| 16 | + | ||
| 17 | + public TopicFactoryAdapter(Topic topicDto, byte[] payload) | ||
| 18 | + { | ||
| 19 | + this.topicDto = topicDto; | ||
| 20 | + this.payload = payload; | ||
| 21 | + } | ||
| 22 | + public static TopicFactoryAdapter createTopicFactory(Topic topicDto, byte[] payload) | ||
| 23 | + { | ||
| 24 | + switch (topicDto.getTopicType()) | ||
| 25 | + { | ||
| 26 | + case "PUT": // 写入 , payload: {"1":{"name1":xxx,"name2":xxx}} | ||
| 27 | + return new PutTopic(topicDto, payload); | ||
| 28 | + case "READ": // 读取 , payload: {"1":{"name1","name2","name3"}} | ||
| 29 | + return new ReadTopic(topicDto, payload); | ||
| 30 | + case "HOST": // 主机操作 , payload: {""} | ||
| 31 | + return new HostTopic(topicDto, payload); | ||
| 32 | + default: | ||
| 33 | + return null; | ||
| 34 | + } | ||
| 35 | + } | ||
| 36 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.data.topic; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.device.modbus.terminal.camera.opf.CameraOperationInstructions; | ||
| 4 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.dto.Topic; | ||
| 5 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt.MqttService; | ||
| 6 | +import com.zhonglai.luhui.device.modbus.terminal.data.TopicFactoryAdapter; | ||
| 7 | +import com.alibaba.fastjson.JSONObject; | ||
| 8 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
| 9 | + | ||
| 10 | +import java.io.*; | ||
| 11 | +import java.net.HttpURLConnection; | ||
| 12 | +import java.net.URL; | ||
| 13 | +import java.nio.file.*; | ||
| 14 | +import java.nio.file.attribute.FileTime; | ||
| 15 | +import java.text.SimpleDateFormat; | ||
| 16 | +import java.util.ArrayList; | ||
| 17 | +import java.util.Date; | ||
| 18 | +import java.util.List; | ||
| 19 | +import java.util.concurrent.atomic.AtomicReference; | ||
| 20 | + | ||
| 21 | +public class HostTopic extends TopicFactoryAdapter { | ||
| 22 | + private static final AtomicReference<Path> currentDir = | ||
| 23 | + new AtomicReference<>(Paths.get(System.getProperty("user.dir"))); | ||
| 24 | + | ||
| 25 | + public HostTopic(Topic topicDto, byte[] payload) { | ||
| 26 | + super(topicDto, payload); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public void parse(MqttService mqttService) { | ||
| 31 | + String str = new String(payload); | ||
| 32 | + JSONObject jsonObject = JSONObject.parseObject(str); | ||
| 33 | + String function = jsonObject.getString("function"); | ||
| 34 | + JSONObject data = jsonObject.getJSONObject("data"); | ||
| 35 | + | ||
| 36 | + JSONObject result = new JSONObject(); | ||
| 37 | + try { | ||
| 38 | + switch (function) { | ||
| 39 | + case "ls": | ||
| 40 | + result.put("result", listFiles()); | ||
| 41 | + break; | ||
| 42 | + case "cd": | ||
| 43 | + result.put("result", changeDirectory(data.getString("path"))); | ||
| 44 | + break; | ||
| 45 | + case "mkdir": | ||
| 46 | + result.put("result", makeDir(data.getString("name"))); | ||
| 47 | + break; | ||
| 48 | + case "rm": | ||
| 49 | + result.put("result", removeFile(data.getString("name"))); | ||
| 50 | + break; | ||
| 51 | + case "copy": | ||
| 52 | + result.put("result", copyFile(data.getString("source"), data.getString("target"))); | ||
| 53 | + break; | ||
| 54 | + case "mk": | ||
| 55 | + result.put("result", makeFile(data.getString("name"))); | ||
| 56 | + break; | ||
| 57 | + case "download": | ||
| 58 | + result.put("result", downloadFile(data.getString("url"), data.getString("name"))); | ||
| 59 | + break; | ||
| 60 | + case "upload": | ||
| 61 | + result.put("result", uploadFile(data.getString("name"), data.getString("url"))); | ||
| 62 | + break; | ||
| 63 | + case "camera": | ||
| 64 | + result.put("result", camera(data)); | ||
| 65 | + break; | ||
| 66 | + | ||
| 67 | + default: | ||
| 68 | + result.put("error", "Unknown function: " + function); | ||
| 69 | + } | ||
| 70 | + } catch (Exception e) { | ||
| 71 | + result.put("error", e.getMessage()); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + // 回传执行结果 | ||
| 75 | + try { | ||
| 76 | + mqttService.publish("HOST_REQ/"+topicDto.getTime(), result.toJSONString()); | ||
| 77 | + } catch (MqttException e) { | ||
| 78 | + logger.info("返回mqtt指令失败"); | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + private JSONObject listFiles() throws IOException { | ||
| 83 | + JSONObject result = new JSONObject(); | ||
| 84 | + Path dir = currentDir.get(); | ||
| 85 | + if (!Files.isDirectory(dir)) { | ||
| 86 | + result.put("error", "Not a directory: " + dir.toString()); | ||
| 87 | + return result; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + // 时间格式化器 | ||
| 91 | + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | ||
| 92 | + | ||
| 93 | + // 文件数组 | ||
| 94 | + List<JSONObject> files = new ArrayList<>(); | ||
| 95 | + Files.list(dir).forEach(path -> { | ||
| 96 | + JSONObject fileObj = new JSONObject(); | ||
| 97 | + fileObj.put("name", path.getFileName().toString()); | ||
| 98 | + try { | ||
| 99 | + FileTime fileTime = Files.getLastModifiedTime(path); | ||
| 100 | + fileObj.put("lastModified", sdf.format(new Date(fileTime.toMillis()))); | ||
| 101 | + } catch (IOException e) { | ||
| 102 | + fileObj.put("lastModified", "unknown"); | ||
| 103 | + } | ||
| 104 | + fileObj.put("isDirectory", Files.isDirectory(path)); | ||
| 105 | + | ||
| 106 | + try { | ||
| 107 | + if (Files.isDirectory(path)) { | ||
| 108 | + fileObj.put("size", "-"); | ||
| 109 | + } else { | ||
| 110 | + fileObj.put("size", Files.size(path)); | ||
| 111 | + } | ||
| 112 | + } catch (IOException e) { | ||
| 113 | + fileObj.put("size", "unknown"); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + files.add(fileObj); | ||
| 117 | + }); | ||
| 118 | + | ||
| 119 | + result.put("path", dir.toAbsolutePath().toString()); | ||
| 120 | + result.put("files", files); | ||
| 121 | + return result; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + | ||
| 125 | + private String changeDirectory(String path) { | ||
| 126 | + Path newPath = currentDir.get().resolve(path).normalize(); | ||
| 127 | + if (Files.isDirectory(newPath)) { | ||
| 128 | + currentDir.set(newPath); | ||
| 129 | + return newPath.toAbsolutePath().toString(); // 返回绝对路径 | ||
| 130 | + } | ||
| 131 | + return ""; | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + private String makeDir(String name) throws IOException { | ||
| 135 | + Path newPath = currentDir.get().resolve(name); | ||
| 136 | + Files.createDirectories(newPath); | ||
| 137 | + return newPath.toString(); | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + private String removeFile(String name) throws IOException { | ||
| 141 | + Path target = currentDir.get().resolve(name); | ||
| 142 | + Files.deleteIfExists(target); | ||
| 143 | + return target.toString(); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + private String copyFile(String source, String target) throws IOException { | ||
| 147 | + Path src = currentDir.get().resolve(source); | ||
| 148 | + Path tgt = currentDir.get().resolve(target); | ||
| 149 | + Files.copy(src, tgt, StandardCopyOption.REPLACE_EXISTING); | ||
| 150 | + return "Copied from " + src.toString() + " to " + tgt.toString(); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + private String makeFile(String name) throws IOException { | ||
| 154 | + Path file = currentDir.get().resolve(name); | ||
| 155 | + Files.createFile(file); | ||
| 156 | + return "File created: " + file.toString(); | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + private String downloadFile(String urlStr, String fileName) throws IOException { | ||
| 160 | + Path target = currentDir.get().resolve(fileName); | ||
| 161 | + | ||
| 162 | + URL url = new URL(urlStr); | ||
| 163 | + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); | ||
| 164 | + conn.setConnectTimeout(10000); | ||
| 165 | + conn.setReadTimeout(10000); | ||
| 166 | + | ||
| 167 | + try (InputStream in = conn.getInputStream(); | ||
| 168 | + OutputStream out = new FileOutputStream(target.toFile())) { | ||
| 169 | + byte[] buffer = new byte[8192]; | ||
| 170 | + int len; | ||
| 171 | + while ((len = in.read(buffer)) != -1) { | ||
| 172 | + out.write(buffer, 0, len); | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | + return "Downloaded file to " + target.toString(); | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + private String uploadFile(String fileName, String urlStr) throws IOException { | ||
| 179 | + Path file = currentDir.get().resolve(fileName); | ||
| 180 | + if (!Files.exists(file)) { | ||
| 181 | + return "File not found: " + file.toString(); | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + String boundary = "----WebKitFormBoundary" + System.currentTimeMillis(); | ||
| 185 | + String LINE_FEED = "\r\n"; | ||
| 186 | + | ||
| 187 | + URL url = new URL(urlStr); | ||
| 188 | + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); | ||
| 189 | + conn.setDoOutput(true); | ||
| 190 | + conn.setRequestMethod("POST"); | ||
| 191 | + conn.setConnectTimeout(10000); | ||
| 192 | + conn.setReadTimeout(10000); | ||
| 193 | + conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); | ||
| 194 | + | ||
| 195 | + try (OutputStream out = conn.getOutputStream(); | ||
| 196 | + PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"), true); | ||
| 197 | + FileInputStream inputStream = new FileInputStream(file.toFile())) { | ||
| 198 | + | ||
| 199 | + // --- 表单文件头 | ||
| 200 | + writer.append("--").append(boundary).append(LINE_FEED); | ||
| 201 | + writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"") | ||
| 202 | + .append(file.getFileName().toString()).append("\"").append(LINE_FEED); | ||
| 203 | + writer.append("Content-Type: application/octet-stream").append(LINE_FEED); | ||
| 204 | + writer.append(LINE_FEED); | ||
| 205 | + writer.flush(); | ||
| 206 | + | ||
| 207 | + // --- 文件内容 | ||
| 208 | + byte[] buffer = new byte[8192]; | ||
| 209 | + int len; | ||
| 210 | + while ((len = inputStream.read(buffer)) != -1) { | ||
| 211 | + out.write(buffer, 0, len); | ||
| 212 | + } | ||
| 213 | + out.flush(); | ||
| 214 | + | ||
| 215 | + // --- 文件结束 | ||
| 216 | + writer.append(LINE_FEED).flush(); | ||
| 217 | + writer.append("--").append(boundary).append("--").append(LINE_FEED); | ||
| 218 | + writer.flush(); | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + int responseCode = conn.getResponseCode(); | ||
| 222 | + String responseMsg = ""; | ||
| 223 | + InputStream respStream = null; | ||
| 224 | + try { | ||
| 225 | + respStream = conn.getInputStream(); | ||
| 226 | + } catch (IOException e) { | ||
| 227 | + respStream = conn.getErrorStream(); // 出错时取 errorStream | ||
| 228 | + } | ||
| 229 | + if (respStream != null) { | ||
| 230 | + StringBuilder sb = new StringBuilder(); | ||
| 231 | + BufferedReader reader = new BufferedReader(new InputStreamReader(respStream, "UTF-8")); | ||
| 232 | + String line; | ||
| 233 | + while ((line = reader.readLine()) != null) { | ||
| 234 | + sb.append(line).append("\n"); | ||
| 235 | + } | ||
| 236 | + responseMsg = sb.toString().trim(); | ||
| 237 | + reader.close(); | ||
| 238 | + } | ||
| 239 | + | ||
| 240 | + return "Uploaded " + file.toString() + " to " + urlStr + | ||
| 241 | + " (HTTP " + responseCode + ") response: " + responseMsg; | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + private String camera(JSONObject parameter) throws IOException { | ||
| 245 | + CameraOperationInstructions cameraOperationInstructions = CameraOperationInstructions.createCameraOperationInstructions( parameter); | ||
| 246 | + if (null != cameraOperationInstructions) | ||
| 247 | + { | ||
| 248 | + return cameraOperationInstructions.execute().toJSONString(); | ||
| 249 | + } | ||
| 250 | + return "camera ok" ; | ||
| 251 | + } | ||
| 252 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.data.topic; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.zhonglai.luhui.device.modbus.terminal.config.InitPlcConfig; | ||
| 5 | +import com.zhonglai.luhui.device.modbus.terminal.data.TopicFactoryAdapter; | ||
| 6 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jWrite; | ||
| 7 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint; | ||
| 8 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.dto.Topic; | ||
| 9 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt.MqttService; | ||
| 10 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
| 11 | + | ||
| 12 | +import java.util.ArrayList; | ||
| 13 | +import java.util.List; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 设备写数据 | ||
| 17 | + * payload: {"1":{"name1":xxx,"name2":xxx}} | ||
| 18 | + */ | ||
| 19 | +public class PutTopic extends TopicFactoryAdapter { | ||
| 20 | + public PutTopic(Topic topicDto, byte[] payload) { | ||
| 21 | + super(topicDto, payload); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + @Override | ||
| 25 | + public void parse(MqttService mqttService) { | ||
| 26 | + String str = new String( payload); | ||
| 27 | + JSONObject jsonObject = JSONObject.parseObject(str); | ||
| 28 | + for (String key : jsonObject.keySet()) | ||
| 29 | + { | ||
| 30 | + JSONObject plcCommand = jsonObject.getJSONObject(key); | ||
| 31 | + Integer id = Integer.parseInt(key); | ||
| 32 | + List<PlcPoint> plcPoints = getPlcPoints(id, plcCommand); | ||
| 33 | + try { | ||
| 34 | + new Modbus4jWrite(id).batchWrite(plcPoints,true); | ||
| 35 | + } catch (Exception e) { | ||
| 36 | + logger.error("写plc异常",e); | ||
| 37 | + } | ||
| 38 | + } | ||
| 39 | + try { | ||
| 40 | + JSONObject rJsonObject = new JSONObject(); | ||
| 41 | + rJsonObject.put("result",1); | ||
| 42 | + mqttService.publish("PUT_REQ/"+topicDto.getTime(),rJsonObject.toJSONString()); | ||
| 43 | + } catch (MqttException e) { | ||
| 44 | + logger.error("饭hi结果异常",e); | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + private List<PlcPoint> getPlcPoints(Integer id,JSONObject plcCommand) | ||
| 49 | + { | ||
| 50 | + List<PlcPoint> plcPoints = new ArrayList<>(); | ||
| 51 | + for (String pointName : plcCommand.keySet()) | ||
| 52 | + { | ||
| 53 | + PlcPoint plcPoint = InitPlcConfig.getPlcSystem(id, pointName); | ||
| 54 | + if ( null != plcPoint) | ||
| 55 | + { | ||
| 56 | + plcPoint.setValue(plcCommand.getString(pointName)); | ||
| 57 | + plcPoints.add(plcPoint); | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | + return plcPoints; | ||
| 61 | + | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.data.topic; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.zhonglai.luhui.device.modbus.terminal.config.InitPlcConfig; | ||
| 5 | +import com.zhonglai.luhui.device.modbus.terminal.data.TopicFactoryAdapter; | ||
| 6 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jRead; | ||
| 7 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jWrite; | ||
| 8 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint; | ||
| 9 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.dto.Topic; | ||
| 10 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt.MqttService; | ||
| 11 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
| 12 | + | ||
| 13 | +import java.util.ArrayList; | ||
| 14 | +import java.util.List; | ||
| 15 | +import java.util.Map; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * 设备读数据 | ||
| 19 | + * payload: {"1":"name1","name2","name3"} | ||
| 20 | + */ | ||
| 21 | +public class ReadTopic extends TopicFactoryAdapter { | ||
| 22 | + public ReadTopic(Topic topicDto, byte[] payload) { | ||
| 23 | + super(topicDto, payload); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + @Override | ||
| 27 | + public void parse(MqttService mqttService) { | ||
| 28 | + JSONObject rJsonObject = new JSONObject(); | ||
| 29 | + String str = new String( payload); | ||
| 30 | + JSONObject jsonObject = JSONObject.parseObject(str); | ||
| 31 | + for (String key : jsonObject.keySet()) | ||
| 32 | + { | ||
| 33 | + String plcCommand = jsonObject.getString(key); | ||
| 34 | + Integer id = Integer.parseInt(key); | ||
| 35 | + List<PlcPoint> plcPoints = getPlcPoints(id, plcCommand); | ||
| 36 | + try { | ||
| 37 | + Map<String, Object> map = new Modbus4jRead(id).batchRead(plcPoints,true); | ||
| 38 | + rJsonObject.put(key, map); | ||
| 39 | + } catch (Exception e) { | ||
| 40 | + logger.error("读plc异常",e); | ||
| 41 | + return; | ||
| 42 | + } | ||
| 43 | + } | ||
| 44 | + try { | ||
| 45 | + mqttService.publish("READ_REQ/"+topicDto.getTime(),rJsonObject.toJSONString()); | ||
| 46 | + } catch (MqttException e) { | ||
| 47 | + logger.error("返回结果异常",e); | ||
| 48 | + } | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + private List<PlcPoint> getPlcPoints(Integer id,String plcCommand) | ||
| 52 | + { | ||
| 53 | + List<PlcPoint> plcPoints = new ArrayList<>(); | ||
| 54 | + for (String pointName : plcCommand.split(",")) | ||
| 55 | + { | ||
| 56 | + PlcPoint plcPoint = InitPlcConfig.getPlcSystem(id, pointName); | ||
| 57 | + if ( null != plcPoint) | ||
| 58 | + { | ||
| 59 | + plcPoints.add(plcPoint); | ||
| 60 | + } | ||
| 61 | + } | ||
| 62 | + return plcPoints; | ||
| 63 | + | ||
| 64 | + } | ||
| 65 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus; | ||
| 2 | + | ||
| 3 | +import com.serotonin.modbus4j.BatchRead; | ||
| 4 | +import com.serotonin.modbus4j.BatchResults; | ||
| 5 | +import com.serotonin.modbus4j.ModbusMaster; | ||
| 6 | +import com.serotonin.modbus4j.code.DataType; | ||
| 7 | +import com.serotonin.modbus4j.exception.ErrorResponseException; | ||
| 8 | +import com.serotonin.modbus4j.exception.ModbusInitException; | ||
| 9 | +import com.serotonin.modbus4j.exception.ModbusTransportException; | ||
| 10 | +import com.serotonin.modbus4j.locator.BaseLocator; | ||
| 11 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint; | ||
| 12 | + | ||
| 13 | +import java.io.File; | ||
| 14 | +import java.util.HashMap; | ||
| 15 | +import java.util.List; | ||
| 16 | +import java.util.Map; | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * modbus通讯工具类,采用modbus4j实现 | ||
| 20 | + * | ||
| 21 | + * @author lxq | ||
| 22 | + * @dependencies modbus4j-3.0.3.jar | ||
| 23 | + * @website https://github.com/infiniteautomation/modbus4j | ||
| 24 | + */ | ||
| 25 | +public class Modbus4jRead { | ||
| 26 | + | ||
| 27 | + private ModbusMaster master; | ||
| 28 | + | ||
| 29 | + public Modbus4jRead(Integer id) throws Exception { | ||
| 30 | + this.master = ModbusMasterMessage.createMaster(id); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 批量读取使用方法 | ||
| 35 | + * | ||
| 36 | + * @throws ModbusTransportException | ||
| 37 | + * @throws ErrorResponseException | ||
| 38 | + * @throws ModbusInitException | ||
| 39 | + */ | ||
| 40 | + /** | ||
| 41 | + * 批量读取点位 | ||
| 42 | + */ | ||
| 43 | + public Map<String, Object> batchRead(List<PlcPoint> plcPoints, boolean zeroBasedAddress) throws ModbusTransportException, ErrorResponseException { | ||
| 44 | + BatchRead<String> batch = new BatchRead<>(); | ||
| 45 | + | ||
| 46 | + for (PlcPoint plcPoint : plcPoints) { | ||
| 47 | + String key = plcPoint.getSystem(); | ||
| 48 | + | ||
| 49 | + // 解析 Modbus 地址,生成 BaseLocator | ||
| 50 | + BaseLocator<?> locator = ModbusAddressParser.parseLocator( | ||
| 51 | + plcPoint.getAddress(), | ||
| 52 | + plcPoint.getDataType(), | ||
| 53 | + plcPoint.getSlaveId(), | ||
| 54 | + zeroBasedAddress, | ||
| 55 | + plcPoint.getOrder() | ||
| 56 | + ); | ||
| 57 | + | ||
| 58 | + batch.addLocator(key, locator); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + batch.setContiguousRequests(false); | ||
| 62 | + | ||
| 63 | + // 发送批量请求 | ||
| 64 | + BatchResults<String> results = master.send(batch); | ||
| 65 | + | ||
| 66 | + // 转换成 JSON 对象 | ||
| 67 | + Map<String, Object> jsonMap = new HashMap<>(); | ||
| 68 | + for (PlcPoint plcPoint : plcPoints) { | ||
| 69 | + | ||
| 70 | + // 输出所有结果 | ||
| 71 | + String key = plcPoint.getSystem(); | ||
| 72 | + Object value = results.getValue(key); | ||
| 73 | + switch (plcPoint.getDataType()) | ||
| 74 | + { | ||
| 75 | + case FLOAT32: | ||
| 76 | + if (value instanceof Integer) { | ||
| 77 | + int raw = (int) value; | ||
| 78 | + switch (plcPoint.getOrder()) { | ||
| 79 | + case "ABCD": | ||
| 80 | + value = ModbusAddressParser.intToFloatABCD(raw); | ||
| 81 | + break; | ||
| 82 | + case "BADC": | ||
| 83 | + value = ModbusAddressParser.intToFloatBADC(raw); | ||
| 84 | + break; | ||
| 85 | + case "CDAB": | ||
| 86 | + value = ModbusAddressParser.intToFloatCDAB(raw); | ||
| 87 | + break; | ||
| 88 | + case "DCBA": | ||
| 89 | + value = ModbusAddressParser.intToFloatDCBA(raw); | ||
| 90 | + break; | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + break; | ||
| 94 | + case INT32: | ||
| 95 | + if (value instanceof Integer) { | ||
| 96 | + int raw = (int) value; | ||
| 97 | + switch (plcPoint.getOrder()) { | ||
| 98 | + case "ABCD": | ||
| 99 | + value = ModbusAddressParser.int32ABCD(raw); | ||
| 100 | + break; | ||
| 101 | + case "BADC": | ||
| 102 | + value = ModbusAddressParser.int32BADC(raw); | ||
| 103 | + break; | ||
| 104 | + case "CDAB": | ||
| 105 | + value = ModbusAddressParser.int32CDAB(raw); | ||
| 106 | + break; | ||
| 107 | + case "DCBA": | ||
| 108 | + value = ModbusAddressParser.int32DCBA(raw); | ||
| 109 | + break; | ||
| 110 | + } | ||
| 111 | + } | ||
| 112 | + break; | ||
| 113 | + } | ||
| 114 | + jsonMap.put(key, value); | ||
| 115 | + } | ||
| 116 | + return jsonMap; | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint; | ||
| 4 | +import org.apache.commons.logging.Log; | ||
| 5 | +import org.apache.commons.logging.LogFactory; | ||
| 6 | + | ||
| 7 | +import com.serotonin.modbus4j.ModbusFactory; | ||
| 8 | +import com.serotonin.modbus4j.ModbusMaster; | ||
| 9 | +import com.serotonin.modbus4j.code.DataType; | ||
| 10 | +import com.serotonin.modbus4j.exception.ErrorResponseException; | ||
| 11 | +import com.serotonin.modbus4j.exception.ModbusInitException; | ||
| 12 | +import com.serotonin.modbus4j.exception.ModbusTransportException; | ||
| 13 | +import com.serotonin.modbus4j.ip.IpParameters; | ||
| 14 | +import com.serotonin.modbus4j.locator.BaseLocator; | ||
| 15 | +import com.serotonin.modbus4j.msg.ModbusResponse; | ||
| 16 | +import com.serotonin.modbus4j.msg.WriteCoilRequest; | ||
| 17 | +import com.serotonin.modbus4j.msg.WriteCoilResponse; | ||
| 18 | +import com.serotonin.modbus4j.msg.WriteCoilsRequest; | ||
| 19 | +import com.serotonin.modbus4j.msg.WriteCoilsResponse; | ||
| 20 | +import com.serotonin.modbus4j.msg.WriteRegisterRequest; | ||
| 21 | +import com.serotonin.modbus4j.msg.WriteRegisterResponse; | ||
| 22 | +import com.serotonin.modbus4j.msg.WriteRegistersRequest; | ||
| 23 | + | ||
| 24 | +import java.util.List; | ||
| 25 | + | ||
| 26 | +/** | ||
| 27 | + * modbus4j写入数据 | ||
| 28 | + * | ||
| 29 | + * @author xq | ||
| 30 | + * | ||
| 31 | + */ | ||
| 32 | +public class Modbus4jWrite { | ||
| 33 | + static Log log = LogFactory.getLog(Modbus4jWrite.class); | ||
| 34 | + private ModbusMaster master; | ||
| 35 | + | ||
| 36 | + public Modbus4jWrite(Integer id) throws Exception { | ||
| 37 | + this.master = ModbusMasterMessage.createMaster(id); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 单点写 | ||
| 43 | + */ | ||
| 44 | + public void writePoint(PlcPoint point, boolean zeroBasedAddress) throws ModbusTransportException, ErrorResponseException { | ||
| 45 | + BaseLocator<?> locator = ModbusAddressParser.parseLocator( | ||
| 46 | + point.getAddress(), | ||
| 47 | + point.getDataType(), | ||
| 48 | + point.getSlaveId(), | ||
| 49 | + zeroBasedAddress, | ||
| 50 | + point.getOrder() | ||
| 51 | + ); | ||
| 52 | + Object value = point.getValue(); | ||
| 53 | + | ||
| 54 | + // 如果 value 是 String,则根据 dataType 转换 | ||
| 55 | + if (value instanceof String) { | ||
| 56 | + String strVal = (String) value; | ||
| 57 | + switch (point.getDataType()) { | ||
| 58 | + case BIT: | ||
| 59 | + value = Boolean.parseBoolean(strVal); | ||
| 60 | + break; | ||
| 61 | + case INT16: | ||
| 62 | + value = Short.parseShort(strVal); | ||
| 63 | + break; | ||
| 64 | + case UINT16: | ||
| 65 | + value = Integer.parseInt(strVal); // Modbus4j 内部支持 unsigned | ||
| 66 | + break; | ||
| 67 | + case INT32: | ||
| 68 | + int raw = Integer.parseInt(strVal); | ||
| 69 | + switch (point.getOrder()) { | ||
| 70 | + case "ABCD": | ||
| 71 | + value = ModbusAddressParser.int32ToABCD(raw); | ||
| 72 | + break; | ||
| 73 | + case "BADC": | ||
| 74 | + value = ModbusAddressParser.int32ToBADC(raw); | ||
| 75 | + break; | ||
| 76 | + case "CDAB": | ||
| 77 | + value = ModbusAddressParser.int32ToCDAB(raw); | ||
| 78 | + break; | ||
| 79 | + case "DCBA": | ||
| 80 | + value = ModbusAddressParser.int32ToDCBA(raw); | ||
| 81 | + break; | ||
| 82 | + default: | ||
| 83 | + value = raw; | ||
| 84 | + break; | ||
| 85 | + } | ||
| 86 | + break; | ||
| 87 | + case INT64: | ||
| 88 | + value = Long.parseLong(strVal); | ||
| 89 | + break; | ||
| 90 | + case FLOAT32: | ||
| 91 | + float fvalue = Float.parseFloat(strVal); | ||
| 92 | + switch (point.getOrder()) { | ||
| 93 | + case "ABCD": | ||
| 94 | + value = ModbusAddressParser.floatToIntABCD(fvalue); | ||
| 95 | + break; | ||
| 96 | + case "BADC": | ||
| 97 | + value = ModbusAddressParser.floatToIntBADC(fvalue); | ||
| 98 | + break; | ||
| 99 | + case "CDAB": | ||
| 100 | + value = ModbusAddressParser.floatToIntCDAB(fvalue); | ||
| 101 | + break; | ||
| 102 | + case "DCBA": | ||
| 103 | + value = ModbusAddressParser.floatToIntDCBA(fvalue); | ||
| 104 | + break; | ||
| 105 | + default: | ||
| 106 | + value = fvalue; | ||
| 107 | + break; | ||
| 108 | + } | ||
| 109 | + break; | ||
| 110 | + case DOUBLE64: | ||
| 111 | + value = Double.parseDouble(strVal); | ||
| 112 | + break; | ||
| 113 | + default: | ||
| 114 | + throw new IllegalArgumentException("不支持的数据类型: " + point.getDataType()); | ||
| 115 | + } | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + // 设置值 | ||
| 119 | + master.setValue(locator, value); | ||
| 120 | + log.info("写入成功: " + point.getName() + " = " + point.getValue()); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + /** | ||
| 124 | + * 批量写(兼容 3.1.0,没有 BatchWrite) | ||
| 125 | + */ | ||
| 126 | + public void batchWrite(List<PlcPoint> points, boolean zeroBasedAddress) throws ModbusTransportException, ErrorResponseException { | ||
| 127 | + | ||
| 128 | + for (PlcPoint plcPoint:points) { | ||
| 129 | + writePoint(plcPoint, zeroBasedAddress); | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + public boolean isConnected() { | ||
| 134 | + return master.isConnected(); | ||
| 135 | + } | ||
| 136 | + public void close() { | ||
| 137 | + master.destroy(); | ||
| 138 | + } | ||
| 139 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus; | ||
| 2 | + | ||
| 3 | +import com.serotonin.modbus4j.locator.BaseLocator; | ||
| 4 | +import com.serotonin.modbus4j.code.DataType; | ||
| 5 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcDataType; | ||
| 6 | + | ||
| 7 | +import java.nio.ByteBuffer; | ||
| 8 | +import java.nio.ByteOrder; | ||
| 9 | + | ||
| 10 | +public class ModbusAddressParser { | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * 解析 Modbus 地址,生成 BaseLocator | ||
| 14 | + * @param rawAddress 原始地址 (40001, 40001.01, 40003-40004) | ||
| 15 | + * @param type 数据类型 (BIT, INT16, UINT16, INT32, INT64, FLOAT32, DOUBLE64) | ||
| 16 | + * @param slaveId 从站ID | ||
| 17 | + * @return BaseLocator 对象 | ||
| 18 | + */ | ||
| 19 | + public static BaseLocator<?> parseLocator(String rawAddress, PlcDataType type, int slaveId, boolean zeroBasedAddress,String order) { | ||
| 20 | + int offset = parseOffset(rawAddress,zeroBasedAddress); | ||
| 21 | + int bitIndex = parseBitIndex(rawAddress); | ||
| 22 | + | ||
| 23 | + switch (type) { | ||
| 24 | + case BIT: | ||
| 25 | + if (rawAddress.startsWith("0")) { | ||
| 26 | + // Coil (线圈输出) | ||
| 27 | + return BaseLocator.coilStatus(slaveId, offset); | ||
| 28 | + } else if (rawAddress.startsWith("1")) { | ||
| 29 | + // Discrete Input (离散量输入) | ||
| 30 | + return BaseLocator.inputStatus(slaveId, offset); | ||
| 31 | + } else if (rawAddress.startsWith("3") && bitIndex >= 0) { | ||
| 32 | + // Input Register 内部 bit 位 | ||
| 33 | + return BaseLocator.inputRegisterBit(slaveId, offset, bitIndex); | ||
| 34 | + } else if (rawAddress.startsWith("4") && bitIndex >= 0) { | ||
| 35 | + // Holding Register 内部 bit 位 | ||
| 36 | + return BaseLocator.holdingRegisterBit(slaveId, offset, bitIndex); | ||
| 37 | + } else { | ||
| 38 | + throw new IllegalArgumentException("地址不支持 BIT 类型: " + rawAddress); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + case INT16: | ||
| 42 | + if (rawAddress.startsWith("3")) { | ||
| 43 | + return BaseLocator.inputRegister(slaveId, offset, DataType.TWO_BYTE_INT_SIGNED); | ||
| 44 | + } else if (rawAddress.startsWith("4")) { | ||
| 45 | + return BaseLocator.holdingRegister(slaveId, offset, DataType.TWO_BYTE_INT_SIGNED); | ||
| 46 | + } | ||
| 47 | + break; | ||
| 48 | + | ||
| 49 | + case UINT16: | ||
| 50 | + if (rawAddress.startsWith("3")) { | ||
| 51 | + return BaseLocator.inputRegister(slaveId, offset, DataType.TWO_BYTE_INT_UNSIGNED); | ||
| 52 | + } else if (rawAddress.startsWith("4")) { | ||
| 53 | + return BaseLocator.holdingRegister(slaveId, offset, DataType.TWO_BYTE_INT_UNSIGNED); | ||
| 54 | + } | ||
| 55 | + break; | ||
| 56 | + | ||
| 57 | + case INT32: | ||
| 58 | + if (rawAddress.startsWith("3")) { | ||
| 59 | + return BaseLocator.inputRegister(slaveId, offset, DataType.FOUR_BYTE_INT_SIGNED); | ||
| 60 | + } else if (rawAddress.startsWith("4")) { | ||
| 61 | + return BaseLocator.holdingRegister(slaveId, offset, DataType.FOUR_BYTE_INT_SIGNED); | ||
| 62 | + } | ||
| 63 | + break; | ||
| 64 | + | ||
| 65 | + case INT64: | ||
| 66 | + if (rawAddress.startsWith("3")) { | ||
| 67 | + return BaseLocator.inputRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_SIGNED); | ||
| 68 | + } else if (rawAddress.startsWith("4")) { | ||
| 69 | + return BaseLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_INT_SIGNED); | ||
| 70 | + } | ||
| 71 | + break; | ||
| 72 | + | ||
| 73 | + case FLOAT32: | ||
| 74 | + if (order == null) { | ||
| 75 | + order = "ABCD"; // 默认大端 | ||
| 76 | + } | ||
| 77 | + int floatDataType = DataType.FOUR_BYTE_FLOAT; | ||
| 78 | + switch (order) { | ||
| 79 | + case "ABCD": // 大端 IEEE754 | ||
| 80 | + floatDataType = DataType.FOUR_BYTE_FLOAT; | ||
| 81 | + break; | ||
| 82 | + case "CDAB": // 寄存器交换 | ||
| 83 | + floatDataType = DataType.FOUR_BYTE_FLOAT_SWAPPED; | ||
| 84 | + break; | ||
| 85 | + case "BADC": // 字节交换 | ||
| 86 | + floatDataType = DataType.FOUR_BYTE_FLOAT_SWAPPED_INVERTED; | ||
| 87 | + break; | ||
| 88 | + case "DCBA": // 全反转 | ||
| 89 | + floatDataType = DataType.FOUR_BYTE_INT_SIGNED_SWAPPED_SWAPPED; | ||
| 90 | + // ⚠ modbus4j 没有直接 DCBA 的 float,可以用 int SWAP-SWAP 再手动转 float | ||
| 91 | + break; | ||
| 92 | + } | ||
| 93 | + if (rawAddress.startsWith("3")) { | ||
| 94 | + return BaseLocator.inputRegister(slaveId, offset, floatDataType); | ||
| 95 | + } else if (rawAddress.startsWith("4")) { | ||
| 96 | + return BaseLocator.holdingRegister(slaveId, offset, floatDataType); | ||
| 97 | + } | ||
| 98 | + break; | ||
| 99 | + | ||
| 100 | + case DOUBLE64: | ||
| 101 | + if (rawAddress.startsWith("3")) { | ||
| 102 | + return BaseLocator.inputRegister(slaveId, offset, DataType.EIGHT_BYTE_FLOAT); | ||
| 103 | + } else if (rawAddress.startsWith("4")) { | ||
| 104 | + return BaseLocator.holdingRegister(slaveId, offset, DataType.EIGHT_BYTE_FLOAT); | ||
| 105 | + } | ||
| 106 | + break; | ||
| 107 | + | ||
| 108 | + default: | ||
| 109 | + throw new IllegalArgumentException("不支持的数据类型: " + type); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + throw new IllegalArgumentException("地址和数据类型不匹配: " + rawAddress + " -> " + type); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + /** 解析寄存器偏移量 */ | ||
| 116 | + public static int parseOffset(String rawAddress, boolean zeroBasedAddress) { | ||
| 117 | + if (rawAddress == null || rawAddress.isEmpty()) { | ||
| 118 | + throw new IllegalArgumentException("地址不能为空"); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + // 处理区间地址:40003-40004 | ||
| 122 | + if (rawAddress.contains("-")) { | ||
| 123 | + String[] parts = rawAddress.split("-"); | ||
| 124 | + rawAddress = parts[0]; // 取起始地址 | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + // 去掉小数点后部分:40001.01 -> 40001 | ||
| 128 | + String basePart = rawAddress.split("\\.")[0]; | ||
| 129 | + int address = Integer.parseInt(basePart); | ||
| 130 | + | ||
| 131 | + int offset; | ||
| 132 | + if (address >= 1 && address <= 9999) { // Coil (0xxxx) | ||
| 133 | + offset = address - 1; | ||
| 134 | + } else if (address >= 10001 && address <= 19999) { // Discrete Input (1xxxx) | ||
| 135 | + offset = address - 10001; | ||
| 136 | + } else if (address >= 30001 && address <= 39999) { // Input Register (3xxxx) | ||
| 137 | + offset = address - 30001; | ||
| 138 | + } else if (address >= 40001 && address <= 49999) { // Holding Register (4xxxx) | ||
| 139 | + offset = address - 40001; | ||
| 140 | + } else { | ||
| 141 | + throw new IllegalArgumentException("无效的Modbus地址: " + rawAddress); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + // 根据 zeroBasedAddress 参数调整偏移 | ||
| 145 | + if (!zeroBasedAddress) { | ||
| 146 | + offset += 1; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + return offset; | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + | ||
| 153 | + /** 解析 bit 索引 */ | ||
| 154 | + public static int parseBitIndex(String rawAddress) { | ||
| 155 | + if (rawAddress.contains(".")) { | ||
| 156 | + String[] parts = rawAddress.split("\\."); | ||
| 157 | + return Integer.parseInt(parts[1]) - 1; // 转成从0开始 | ||
| 158 | + } | ||
| 159 | + return -1; // 没有 bit 位 | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + public static float intToFloatDCBA(int value) { | ||
| 163 | + byte[] bytes = new byte[4]; | ||
| 164 | + bytes[0] = (byte) (value & 0xFF); | ||
| 165 | + bytes[1] = (byte) ((value >> 8) & 0xFF); | ||
| 166 | + bytes[2] = (byte) ((value >> 16) & 0xFF); | ||
| 167 | + bytes[3] = (byte) ((value >> 24) & 0xFF); | ||
| 168 | + | ||
| 169 | + // 反转成 DCBA | ||
| 170 | + byte tmp; | ||
| 171 | + tmp = bytes[0]; bytes[0] = bytes[3]; bytes[3] = tmp; | ||
| 172 | + tmp = bytes[1]; bytes[1] = bytes[2]; bytes[2] = tmp; | ||
| 173 | + | ||
| 174 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat(); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + public static int floatToIntDCBA(float f) { | ||
| 178 | + byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(f).array(); | ||
| 179 | + | ||
| 180 | + // 反转成 DCBA | ||
| 181 | + byte tmp; | ||
| 182 | + tmp = bytes[0]; bytes[0] = bytes[3]; bytes[3] = tmp; | ||
| 183 | + tmp = bytes[1]; bytes[1] = bytes[2]; bytes[2] = tmp; | ||
| 184 | + | ||
| 185 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 186 | + } | ||
| 187 | + | ||
| 188 | + | ||
| 189 | + public static float intToFloatABCD(int value) { | ||
| 190 | + byte[] bytes = intToBytes(value); | ||
| 191 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat(); | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + public static float intToFloatBADC(int value) { | ||
| 195 | + byte[] bytes = intToBytes(value); | ||
| 196 | + swap(bytes, 0, 1); | ||
| 197 | + swap(bytes, 2, 3); | ||
| 198 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat(); | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + public static float intToFloatCDAB(int value) { | ||
| 202 | + byte[] bytes = intToBytes(value); | ||
| 203 | + swap(bytes, 0, 2); | ||
| 204 | + swap(bytes, 1, 3); | ||
| 205 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat(); | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + // ========== 32位整型的不同字节序 ========== | ||
| 209 | + public static int int32ABCD(int value) { | ||
| 210 | + return value; // 默认就是ABCD | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + public static int int32BADC(int value) { | ||
| 214 | + byte[] bytes = intToBytes(value); | ||
| 215 | + swap(bytes, 0, 1); | ||
| 216 | + swap(bytes, 2, 3); | ||
| 217 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + public static int int32CDAB(int value) { | ||
| 221 | + byte[] bytes = intToBytes(value); | ||
| 222 | + swap(bytes, 0, 2); | ||
| 223 | + swap(bytes, 1, 3); | ||
| 224 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + public static int int32DCBA(int value) { | ||
| 228 | + byte[] bytes = intToBytes(value); | ||
| 229 | + reverse(bytes); | ||
| 230 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 231 | + } | ||
| 232 | + | ||
| 233 | + // ========== 通用工具 ========== | ||
| 234 | + private static byte[] intToBytes(int value) { | ||
| 235 | + return new byte[] { | ||
| 236 | + (byte) ((value >> 24) & 0xFF), | ||
| 237 | + (byte) ((value >> 16) & 0xFF), | ||
| 238 | + (byte) ((value >> 8) & 0xFF), | ||
| 239 | + (byte) (value & 0xFF) | ||
| 240 | + }; | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + private static void swap(byte[] arr, int i, int j) { | ||
| 244 | + byte tmp = arr[i]; | ||
| 245 | + arr[i] = arr[j]; | ||
| 246 | + arr[j] = tmp; | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + private static void reverse(byte[] arr) { | ||
| 250 | + for (int i = 0, j = arr.length - 1; i < j; i++, j--) { | ||
| 251 | + swap(arr, i, j); | ||
| 252 | + } | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + | ||
| 256 | + // ========== Float 写入 ========== | ||
| 257 | + public static int floatToIntABCD(float f) { | ||
| 258 | + byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(f).array(); | ||
| 259 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + public static int floatToIntBADC(float f) { | ||
| 263 | + byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(f).array(); | ||
| 264 | + swap(bytes, 0, 1); | ||
| 265 | + swap(bytes, 2, 3); | ||
| 266 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 267 | + } | ||
| 268 | + | ||
| 269 | + public static int floatToIntCDAB(float f) { | ||
| 270 | + byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putFloat(f).array(); | ||
| 271 | + swap(bytes, 0, 2); | ||
| 272 | + swap(bytes, 1, 3); | ||
| 273 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + | ||
| 277 | + // ========== Int32 写入 ========== | ||
| 278 | + public static int int32ToABCD(int value) { | ||
| 279 | + return value; // 默认 | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + public static int int32ToBADC(int value) { | ||
| 283 | + byte[] bytes = intToBytes(value); | ||
| 284 | + swap(bytes, 0, 1); | ||
| 285 | + swap(bytes, 2, 3); | ||
| 286 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 287 | + } | ||
| 288 | + | ||
| 289 | + public static int int32ToCDAB(int value) { | ||
| 290 | + byte[] bytes = intToBytes(value); | ||
| 291 | + swap(bytes, 0, 2); | ||
| 292 | + swap(bytes, 1, 3); | ||
| 293 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 294 | + } | ||
| 295 | + | ||
| 296 | + public static int int32ToDCBA(int value) { | ||
| 297 | + byte[] bytes = intToBytes(value); | ||
| 298 | + reverse(bytes); | ||
| 299 | + return ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getInt(); | ||
| 300 | + } | ||
| 301 | + | ||
| 302 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.serotonin.modbus4j.ModbusFactory; | ||
| 5 | +import com.serotonin.modbus4j.ModbusMaster; | ||
| 6 | +import com.serotonin.modbus4j.exception.ModbusInitException; | ||
| 7 | +import com.serotonin.modbus4j.exception.ModbusTransportException; | ||
| 8 | +import com.serotonin.modbus4j.ip.IpParameters; | ||
| 9 | +import com.serotonin.modbus4j.msg.*; | ||
| 10 | +import com.zhonglai.luhui.device.modbus.terminal.config.InitPlcConfig; | ||
| 11 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.CachPlcConfig; | ||
| 12 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.JSerialCommWrapper; | ||
| 13 | + | ||
| 14 | +import java.io.File; | ||
| 15 | +import java.util.concurrent.ConcurrentHashMap; | ||
| 16 | + | ||
| 17 | +public class ModbusMasterMessage { | ||
| 18 | + /** | ||
| 19 | + * 工厂 | ||
| 20 | + */ | ||
| 21 | + static ModbusFactory modbusFactory; | ||
| 22 | + static { | ||
| 23 | + if (modbusFactory == null) { | ||
| 24 | + modbusFactory = new ModbusFactory(); | ||
| 25 | + } | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + // 存储每个系统的 ModbusMaster | ||
| 29 | + private static final ConcurrentHashMap<Integer, ModbusMaster> masterCache = new ConcurrentHashMap<>(); | ||
| 30 | + // 每个系统 ID 对应的锁对象 | ||
| 31 | + private static final ConcurrentHashMap<Integer, Object> lockMap = new ConcurrentHashMap<>(); | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 创建或获取 ModbusMaster (线程安全) | ||
| 37 | + */ | ||
| 38 | + public static ModbusMaster createMaster(Integer id) throws Exception { | ||
| 39 | + ModbusMaster master = masterCache.get(id); | ||
| 40 | + if (master != null && master.isConnected()) { | ||
| 41 | + return master; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + // 获取该 ID 的专用锁 | ||
| 45 | + Object lock = lockMap.computeIfAbsent(id, k -> new Object()); | ||
| 46 | + | ||
| 47 | + synchronized (lock) { | ||
| 48 | + // 双重检查,避免重复创建 | ||
| 49 | + master = masterCache.get(id); | ||
| 50 | + if (master != null && master.isConnected()) { | ||
| 51 | + return master; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + // 如果旧的存在但断开了,销毁 | ||
| 55 | + if (master != null) { | ||
| 56 | + master.destroy(); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + // 创建新 master | ||
| 60 | + ModbusMaster newMaster = selectMaster(id); | ||
| 61 | +// newMaster.setIoLog(new MyIOLog(new File("logs/modbus-" + id + ".log"))); | ||
| 62 | + newMaster.init(); | ||
| 63 | + | ||
| 64 | + masterCache.put(id, newMaster); | ||
| 65 | + return newMaster; | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + | ||
| 70 | + private static ModbusMaster selectMaster(Integer id) throws Exception { | ||
| 71 | + | ||
| 72 | + CachPlcConfig cachPlcConfig = InitPlcConfig.getPlcSystems(id); | ||
| 73 | + if (null == cachPlcConfig) | ||
| 74 | + { | ||
| 75 | + throw new Exception("系统: " + id+" 未配置"); | ||
| 76 | + } | ||
| 77 | + JSONObject connectConfig = cachPlcConfig.getConnectConfig(); | ||
| 78 | + | ||
| 79 | + switch (cachPlcConfig.getProtocolType()) | ||
| 80 | + { | ||
| 81 | + case RTU: | ||
| 82 | + return modbusFactory.createRtuMaster(JSONObject.parseObject(connectConfig.toJSONString(),JSerialCommWrapper.class)); | ||
| 83 | + case TCP: | ||
| 84 | + return modbusFactory.createTcpMaster(JSONObject.parseObject(connectConfig.toJSONString(),IpParameters.class), true); //这里传的是 false → 表示短连接模式,每次用完连接就会关闭。 | ||
| 85 | + case ASCII: | ||
| 86 | + return modbusFactory.createAsciiMaster(JSONObject.parseObject(connectConfig.toJSONString(),JSerialCommWrapper.class)); | ||
| 87 | + case UDP: | ||
| 88 | + return modbusFactory.createUdpMaster(JSONObject.parseObject(connectConfig.toJSONString(),IpParameters.class)); | ||
| 89 | + default: | ||
| 90 | + throw new Exception("系统: " + id+" 不支持的协议"); | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + /** | ||
| 95 | + * 关闭指定系统的 Master | ||
| 96 | + */ | ||
| 97 | + public static void closeMaster(Integer id) { | ||
| 98 | + Object lock = lockMap.computeIfAbsent(id, k -> new Object()); | ||
| 99 | + synchronized (lock) { | ||
| 100 | + ModbusMaster master = masterCache.remove(id); | ||
| 101 | + if (master != null) { | ||
| 102 | + master.destroy(); | ||
| 103 | + } | ||
| 104 | + lockMap.remove(id); | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * 关闭所有 Master | ||
| 110 | + */ | ||
| 111 | + public static void closeAll() { | ||
| 112 | + masterCache.values().forEach(ModbusMaster::destroy); | ||
| 113 | + masterCache.clear(); | ||
| 114 | + lockMap.clear(); | ||
| 115 | + } | ||
| 116 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus; | ||
| 2 | + | ||
| 3 | +import com.serotonin.modbus4j.sero.log.BaseIOLog; | ||
| 4 | + | ||
| 5 | +import java.io.File; | ||
| 6 | + | ||
| 7 | +public class MyIOLog extends BaseIOLog { | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + /** | ||
| 11 | + * <p>Constructor for BaseIOLog.</p> | ||
| 12 | + * | ||
| 13 | + * @param logFile a {@link File} object. | ||
| 14 | + */ | ||
| 15 | + public MyIOLog(File logFile) { | ||
| 16 | + super(logFile); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + @Override | ||
| 20 | + protected void sizeCheck() { | ||
| 21 | + // 简单示例:不限制大小 | ||
| 22 | + // 可以实现文件超过一定大小时备份、清空等逻辑 | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public void log(String message) { | ||
| 27 | + super.log(message); | ||
| 28 | + // 同时在控制台打印 | ||
| 29 | + System.out.println(message); | ||
| 30 | + } | ||
| 31 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus.dto; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.fasterxml.jackson.databind.util.JSONPObject; | ||
| 5 | + | ||
| 6 | +import java.util.Map; | ||
| 7 | + | ||
| 8 | +public class CachPlcConfig { | ||
| 9 | + private Integer id; | ||
| 10 | + private String systemName; | ||
| 11 | + private ProtocolType protocolType; | ||
| 12 | + private JSONObject connectConfig; | ||
| 13 | + private Map<String, PlcPoint> plcMap; | ||
| 14 | + | ||
| 15 | + public String getSystemName() { | ||
| 16 | + return systemName; | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + public void setSystemName(String systemName) { | ||
| 20 | + this.systemName = systemName; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + public Map<String, PlcPoint> getPlcMap() { | ||
| 24 | + return plcMap; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + public void setPlcMap(Map<String, PlcPoint> plcMap) { | ||
| 28 | + this.plcMap = plcMap; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + public JSONObject getConnectConfig() { | ||
| 32 | + return connectConfig; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public void setConnectConfig(JSONObject connectConfig) { | ||
| 36 | + this.connectConfig = connectConfig; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + public ProtocolType getProtocolType() { | ||
| 40 | + return protocolType; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + public void setProtocolType(ProtocolType protocolType) { | ||
| 44 | + this.protocolType = protocolType; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public Integer getId() { | ||
| 48 | + return id; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + public void setId(Integer id) { | ||
| 52 | + this.id = id; | ||
| 53 | + } | ||
| 54 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus.dto; | ||
| 2 | + | ||
| 3 | +import com.serotonin.modbus4j.serial.SerialPortWrapper; | ||
| 4 | +import com.fazecast.jSerialComm.SerialPort; // 需要 jSerialComm 库 | ||
| 5 | + | ||
| 6 | +import java.io.InputStream; | ||
| 7 | +import java.io.OutputStream; | ||
| 8 | + | ||
| 9 | +public class JSerialCommWrapper implements SerialPortWrapper { | ||
| 10 | + private final String commPortId; // 串口号,例如 "COM3" 或 "/dev/ttyUSB0" | ||
| 11 | + private final int baudRate; // 波特率 | ||
| 12 | + private final int dataBits; | ||
| 13 | + private final int stopBits; | ||
| 14 | + private final int parity; // 0 = NONE, 1 = ODD, 2 = EVEN | ||
| 15 | + | ||
| 16 | + private SerialPort serialPort; | ||
| 17 | + | ||
| 18 | + public JSerialCommWrapper(String commPortId, int baudRate, int dataBits, int stopBits, int parity) { | ||
| 19 | + this.commPortId = commPortId; | ||
| 20 | + this.baudRate = baudRate; | ||
| 21 | + this.dataBits = dataBits; | ||
| 22 | + this.stopBits = stopBits; | ||
| 23 | + this.parity = parity; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + @Override | ||
| 27 | + public void close() throws Exception { | ||
| 28 | + if (serialPort != null) { | ||
| 29 | + serialPort.closePort(); | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + @Override | ||
| 34 | + public void open() throws Exception { | ||
| 35 | + serialPort = SerialPort.getCommPort(commPortId); | ||
| 36 | + serialPort.setComPortParameters(baudRate, dataBits, stopBits, parity); | ||
| 37 | + serialPort.openPort(); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + public InputStream getInputStream() { | ||
| 42 | + return serialPort.getInputStream(); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @Override | ||
| 46 | + public OutputStream getOutputStream() { | ||
| 47 | + return serialPort.getOutputStream(); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + @Override | ||
| 51 | + public int getBaudRate() { | ||
| 52 | + return baudRate; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + @Override | ||
| 56 | + public int getDataBits() { | ||
| 57 | + return dataBits; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + @Override | ||
| 61 | + public int getStopBits() { | ||
| 62 | + return stopBits; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + @Override | ||
| 66 | + public int getParity() { | ||
| 67 | + return parity; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus.dto; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.annotation.JsonCreator; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * PLC读写权限 | ||
| 7 | + */ | ||
| 8 | +public enum PlcAccess { | ||
| 9 | + R, | ||
| 10 | + W, | ||
| 11 | + RW; | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + @JsonCreator | ||
| 15 | + public static PlcAccess fromString(String value) { | ||
| 16 | + if (value == null) return null; | ||
| 17 | + switch (value.toUpperCase()) { | ||
| 18 | + case "R": | ||
| 19 | + return R; | ||
| 20 | + case "W": | ||
| 21 | + return W; | ||
| 22 | + case "RW": | ||
| 23 | + return RW; | ||
| 24 | + default: | ||
| 25 | + throw new IllegalArgumentException("未知的枚举值: " + value); | ||
| 26 | + } | ||
| 27 | + } | ||
| 28 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus.dto; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.annotation.JsonCreator; | ||
| 4 | + | ||
| 5 | +public enum PlcDataType { | ||
| 6 | + BIT, // 线圈/开关量 | ||
| 7 | + INPUT_BIT, // 输入离散量 | ||
| 8 | + INT16, | ||
| 9 | + UINT16, | ||
| 10 | + INT32, | ||
| 11 | + INT64, | ||
| 12 | + FLOAT32, | ||
| 13 | + DOUBLE64; | ||
| 14 | + | ||
| 15 | + //忽略大小写 | ||
| 16 | + @JsonCreator | ||
| 17 | + public static PlcDataType fromString(String value) { | ||
| 18 | + if (value == null) return null; | ||
| 19 | + switch (value.toUpperCase()) { | ||
| 20 | + case "BIT": | ||
| 21 | + return BIT; | ||
| 22 | + case "INT": | ||
| 23 | + case "INT16": | ||
| 24 | + return INT16; | ||
| 25 | + case "UINT": | ||
| 26 | + case "UINT16": | ||
| 27 | + return UINT16; | ||
| 28 | + case "LONG": // 兼容 long | ||
| 29 | + return INT32; | ||
| 30 | + case "INT32": | ||
| 31 | + return INT32; | ||
| 32 | + case "INT64": | ||
| 33 | + return INT64; | ||
| 34 | + case "FLOAT": | ||
| 35 | + case "FLOAT32": | ||
| 36 | + return FLOAT32; | ||
| 37 | + case "DOUBLE": | ||
| 38 | + case "DOUBLE64": | ||
| 39 | + return DOUBLE64; | ||
| 40 | + case "INPUT_BIT": | ||
| 41 | + case "INPUT": | ||
| 42 | + return INPUT_BIT; | ||
| 43 | + default: | ||
| 44 | + throw new IllegalArgumentException("未知的枚举值: " + value); | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | +} | ||
| 48 | + |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus.dto; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +public class PlcPoint { | ||
| 5 | + public String name; // 点位名 | ||
| 6 | + public String system; // 点位系统名 | ||
| 7 | + public String address; // Modbus 地址,如 40001, 40001.01, 40003-40004 | ||
| 8 | + public PlcDataType dataType; // bit, int16, uint16, int32, float32, double64 | ||
| 9 | + public Integer slaveId = 1; | ||
| 10 | + public Object value; | ||
| 11 | + private String order = "DCBA"; // 新增,默认值 | ||
| 12 | + private PlcAccess access; | ||
| 13 | + | ||
| 14 | + public PlcAccess getAccess() { | ||
| 15 | + return access; | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + public void setAccess(PlcAccess access) { | ||
| 19 | + this.access = access; | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + public String getOrder() { | ||
| 23 | + return order; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + public void setOrder(String order) { | ||
| 27 | + this.order = order; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public Object getValue() { | ||
| 31 | + return value; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + public void setValue(Object value) { | ||
| 35 | + this.value = value; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + public String getName() { | ||
| 39 | + return name; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public void setName(String name) { | ||
| 43 | + this.name = name; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + public String getSystem() { | ||
| 47 | + return system; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + public void setSystem(String system) { | ||
| 51 | + this.system = system; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + public String getAddress() { | ||
| 55 | + return address; | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + public void setAddress(String address) { | ||
| 59 | + this.address = address; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + public PlcDataType getDataType() { | ||
| 63 | + return dataType; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + public void setDataType(PlcDataType dataType) { | ||
| 67 | + this.dataType = dataType; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + public Integer getSlaveId() { | ||
| 71 | + return slaveId; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + public void setSlaveId(Integer slaveId) { | ||
| 75 | + this.slaveId = slaveId; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.modbus.dto; | ||
| 2 | +import com.alibaba.fastjson.JSONObject; | ||
| 3 | + | ||
| 4 | +import java.util.List; | ||
| 5 | + | ||
| 6 | +public class PlcSystem { | ||
| 7 | + private Integer id; | ||
| 8 | + private String systemName; | ||
| 9 | + private ProtocolType protocolType; | ||
| 10 | + private JSONObject connectConfig; | ||
| 11 | + private List<PlcPoint> points; | ||
| 12 | + | ||
| 13 | + public String getSystemName() { | ||
| 14 | + return systemName; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + public void setSystemName(String systemName) { | ||
| 18 | + this.systemName = systemName; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + public ProtocolType getProtocolType() { | ||
| 22 | + return protocolType; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + public void setProtocolType(ProtocolType protocolType) { | ||
| 26 | + this.protocolType = protocolType; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + public JSONObject getConnectConfig() { | ||
| 30 | + return connectConfig; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public void setConnectConfig(JSONObject connectConfig) { | ||
| 34 | + this.connectConfig = connectConfig; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public List<PlcPoint> getPoints() { | ||
| 38 | + return points; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + public void setPoints(List<PlcPoint> points) { | ||
| 42 | + this.points = points; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + public Integer getId() { | ||
| 46 | + return id; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public void setId(Integer id) { | ||
| 50 | + this.id = id; | ||
| 51 | + } | ||
| 52 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.task; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.serotonin.modbus4j.exception.ErrorResponseException; | ||
| 5 | +import com.serotonin.modbus4j.exception.ModbusTransportException; | ||
| 6 | +import com.zhonglai.luhui.device.modbus.terminal.config.InitPlcConfig; | ||
| 7 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jRead; | ||
| 8 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.ModbusMasterMessage; | ||
| 9 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.CachPlcConfig; | ||
| 10 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint; | ||
| 11 | +import com.zhonglai.luhui.device.mqtt.terminal.jar.mqtt.MqttService; | ||
| 12 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
| 13 | +import org.slf4j.Logger; | ||
| 14 | +import org.slf4j.LoggerFactory; | ||
| 15 | + | ||
| 16 | +import java.util.ArrayList; | ||
| 17 | +import java.util.List; | ||
| 18 | +import java.util.Map; | ||
| 19 | +import java.util.concurrent.TimeUnit; | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * 定时采集上报plc数据 | ||
| 23 | + */ | ||
| 24 | +public class CollectPlcDataTask { | ||
| 25 | + protected static final Logger logger = LoggerFactory.getLogger(CollectPlcDataTask.class); | ||
| 26 | + | ||
| 27 | + private static final int maxDataLenth = 30; | ||
| 28 | + public void collect(MqttService mqttService) { | ||
| 29 | + ScheduledThreadPool.scheduler.scheduleAtFixedRate(() -> { | ||
| 30 | + try { | ||
| 31 | + pubMqttData(mqttService); | ||
| 32 | + }catch (Exception e) | ||
| 33 | + { | ||
| 34 | + logger.info("plc通讯异常:{}",e.getMessage()); | ||
| 35 | + } | ||
| 36 | + },0, 60, TimeUnit.SECONDS); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + private void pubMqttData(MqttService mqttService) | ||
| 40 | + { | ||
| 41 | + //查看可以访问的plc | ||
| 42 | + Map<Integer, CachPlcConfig> plcConfigMap = InitPlcConfig.getPlcsConfigMap(); | ||
| 43 | + for (Integer plcId : plcConfigMap.keySet()) | ||
| 44 | + { | ||
| 45 | + CachPlcConfig cachPlcConfig = plcConfigMap.get(plcId); | ||
| 46 | + try { | ||
| 47 | + pubMqttData(mqttService, plcId, cachPlcConfig); | ||
| 48 | + } catch (Exception e) { | ||
| 49 | + logger.info("plc {} 通讯异常:{}",plcId,e.getMessage()); | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + private void pubMqttData(MqttService mqttService, Integer plcId,CachPlcConfig cachPlcConfig) throws Exception | ||
| 55 | + { | ||
| 56 | + Map<String, PlcPoint> map = cachPlcConfig.getPlcMap(); | ||
| 57 | + | ||
| 58 | + List<PlcPoint> plcPoints = new ArrayList<>(); | ||
| 59 | + | ||
| 60 | + int dataLenth = 1 ; | ||
| 61 | + for (String system : map.keySet()) | ||
| 62 | + { | ||
| 63 | + PlcPoint plcPoint = map.get(system); | ||
| 64 | + plcPoints.add(plcPoint); | ||
| 65 | + dataLenth++; | ||
| 66 | + if (dataLenth == maxDataLenth) | ||
| 67 | + { | ||
| 68 | + if(!subMqttData(mqttService, plcId, plcPoints)) | ||
| 69 | + { | ||
| 70 | + plcPoints.clear(); | ||
| 71 | + break; | ||
| 72 | + } | ||
| 73 | + System.out.println("plc "+plcId+" 发送数据 "+plcPoints.size()+" 条,等待1s接着发送"); | ||
| 74 | + dataLenth = 1; | ||
| 75 | + plcPoints.clear(); | ||
| 76 | + plcPoints = new ArrayList<>(); | ||
| 77 | + try { | ||
| 78 | + Thread.sleep(1000l); | ||
| 79 | + } catch (InterruptedException e) { | ||
| 80 | + throw new RuntimeException(e); | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + if (plcPoints.size() > 0) | ||
| 86 | + { | ||
| 87 | + subMqttData(mqttService, plcId, plcPoints); | ||
| 88 | + System.out.println("plc "+plcId+" 发送数据 "+plcPoints.size()+" 条"); | ||
| 89 | + } | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + private boolean subMqttData(MqttService mqttService, Integer plcId, List<PlcPoint> plcPoints) | ||
| 93 | + { | ||
| 94 | + //通知 | ||
| 95 | + try { | ||
| 96 | + Map<String, Object> datMap = new Modbus4jRead(plcId).batchRead(plcPoints,true); | ||
| 97 | + JSONObject mqttSendData = new JSONObject(); | ||
| 98 | + mqttSendData.put(plcId+"", datMap); | ||
| 99 | + String rdata = JSONObject.toJSONString(mqttSendData); | ||
| 100 | + mqttService.publish("ADD_POST",rdata); | ||
| 101 | + } catch (Exception e) { | ||
| 102 | + if (e instanceof ModbusTransportException ) | ||
| 103 | + { | ||
| 104 | + logger.error("plc通讯超时:"+plcId); | ||
| 105 | + ModbusMasterMessage.closeMaster(plcId); // 销毁旧连接 | ||
| 106 | + return false; | ||
| 107 | + }else | ||
| 108 | + if(e instanceof MqttException) | ||
| 109 | + { | ||
| 110 | + logger.error("mqtt连接异常",e); | ||
| 111 | + }else | ||
| 112 | + if (e instanceof ErrorResponseException) | ||
| 113 | + { | ||
| 114 | + logger.error("plc返回指令异常",e); | ||
| 115 | + ModbusMasterMessage.closeMaster(plcId); // 销毁旧连接 | ||
| 116 | + return false; | ||
| 117 | + }else{ | ||
| 118 | + throw new RuntimeException(e); | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + return true; | ||
| 122 | + } | ||
| 123 | +} |
| 1 | +package com.zhonglai.luhui.device.modbus.terminal.task; | ||
| 2 | + | ||
| 3 | +import java.util.concurrent.Executors; | ||
| 4 | +import java.util.concurrent.ScheduledExecutorService; | ||
| 5 | + | ||
| 6 | +public class ScheduledThreadPool { | ||
| 7 | + public static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); | ||
| 8 | + | ||
| 9 | + public static void stopScheduler() { | ||
| 10 | + if (null != scheduler) | ||
| 11 | + { | ||
| 12 | + scheduler.shutdown(); | ||
| 13 | + } | ||
| 14 | + } | ||
| 15 | +} |
| 1 | +{ | ||
| 2 | + "plcs": [ | ||
| 3 | + { | ||
| 4 | + "id": 6, | ||
| 5 | + "systemName": "测试", | ||
| 6 | + "protocolType": "TCP", | ||
| 7 | + "connectConfig": { "host": "192.168.1.82", "port": 2000}, | ||
| 8 | + "points": [ | ||
| 9 | + {"name": "水泵1故障", "system": "sb1gz", "address": "40001.01", "dataType": "bit","access":"r"}, | ||
| 10 | + {"name": "水泵2故障", "system": "sb2gz", "address": "40001.02", "dataType": "bit","access":"r"}, | ||
| 11 | + {"name": "氧锥泵1故障", "system": "yzb1gz", "address": "40001.03", "dataType": "bit","access":"r"}, | ||
| 12 | + {"name": "氧锥泵2故障", "system": "yzb2gz", "address": "40001.04", "dataType": "bit","access":"r"}, | ||
| 13 | + {"name": "氧锥泵3故障", "system": "yzb3gz", "address": "40001.05", "dataType": "bit","access":"r"}, | ||
| 14 | + {"name": "氧锥泵4故障", "system": "yzb4gz", "address": "40001.06", "dataType": "bit","access":"r"}, | ||
| 15 | + {"name": "排污泵故障", "system": "pwb_gz", "address": "40001.07", "dataType": "bit","access":"r"}, | ||
| 16 | + {"name": "排污阀1开不到位", "system": "pwf1kbdw", "address": "40001.09", "dataType": "bit","access":"r"}, | ||
| 17 | + {"name": "溶氧上限报警设定值", "system": "ry_sxsz", "address": "40053-40054", "dataType": "float32","order": "CDAB","access":"rw"}, | ||
| 18 | + {"name": "溶氧下限报警设定值", "system": "ry_xxsz", "address": "40055-40056", "dataType": "float32","order": "CDAB","access":"rw"} | ||
| 19 | + ] | ||
| 20 | + }, | ||
| 21 | + { | ||
| 22 | + "id": 1, | ||
| 23 | + "systemName": "成鱼系统1", | ||
| 24 | + "protocolType": "TCP", | ||
| 25 | + "connectConfig": { "host": "192.168.1.19", "port": 2000}, | ||
| 26 | + "points": [ | ||
| 27 | + {"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"}, | ||
| 28 | + {"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"}, | ||
| 29 | + {"name": "补水泵启动", "system": "bsbqd", "address": "10003", "dataType": "bit"}, | ||
| 30 | + {"name": "水泵1运行", "system": "sb1yx", "address": "10004", "dataType": "bit"}, | ||
| 31 | + {"name": "水泵2运行", "system": "sb2yx", "address": "10005", "dataType": "bit"}, | ||
| 32 | + {"name": "氧锥泵1运行", "system": "yzb1yx", "address": "10006", "dataType": "bit"}, | ||
| 33 | + {"name": "氧锥泵2运行", "system": "yzb2yx", "address": "10007", "dataType": "bit"}, | ||
| 34 | + {"name": "氧锥泵3运行", "system": "yzb3yx", "address": "10008", "dataType": "bit"}, | ||
| 35 | + {"name": "氧锥泵4运行", "system": "yzb4yx", "address": "10009", "dataType": "bit"}, | ||
| 36 | + {"name": "排污泵运行", "system": "pwb", "address": "10010", "dataType": "bit"}, | ||
| 37 | + {"name": "微滤机电源合闸", "system": "wljdyhz", "address": "10011", "dataType": "bit"}, | ||
| 38 | + {"name": "紫外灯电源合闸", "system": "zwd", "address": "10012", "dataType": "bit"}, | ||
| 39 | + {"name": "微滤池液位高", "system": "wlcyw_g", "address": "10013", "dataType": "bit"}, | ||
| 40 | + {"name": "微滤池液位低", "system": "wlcyw_d", "address": "10014", "dataType": "bit"}, | ||
| 41 | + {"name": "蝶阀1开到位", "system": "df1kdw", "address": "10015", "dataType": "bit"}, | ||
| 42 | + {"name": "蝶阀1关到位", "system": "df1gdw", "address": "10016", "dataType": "bit"}, | ||
| 43 | + {"name": "蝶阀2开到位", "system": "df2kdw", "address": "10017", "dataType": "bit"}, | ||
| 44 | + {"name": "蝶阀2关到位", "system": "df2gdw", "address": "10018", "dataType": "bit"}, | ||
| 45 | + {"name": "蝶阀3开到位", "system": "df3kdw", "address": "10019", "dataType": "bit"}, | ||
| 46 | + {"name": "蝶阀3关到位", "system": "df3gdw", "address": "10020", "dataType": "bit"}, | ||
| 47 | + {"name": "蝶阀4开到位", "system": "df4kdw", "address": "10021", "dataType": "bit"}, | ||
| 48 | + {"name": "蝶阀4关到位", "system": "df4gdw", "address": "10022", "dataType": "bit"}, | ||
| 49 | + {"name": "蝶阀5开到位", "system": "df5kdw", "address": "10023", "dataType": "bit"}, | ||
| 50 | + {"name": "蝶阀5关到位", "system": "df5gdw", "address": "10024", "dataType": "bit"}, | ||
| 51 | + {"name": "蝶阀6开到位", "system": "df6kdw", "address": "10025", "dataType": "bit"}, | ||
| 52 | + {"name": "蝶阀6关到位", "system": "df6gdw", "address": "10026", "dataType": "bit"}, | ||
| 53 | + {"name": "蝶阀7开到位", "system": "df7kdw", "address": "10027", "dataType": "bit"}, | ||
| 54 | + {"name": "蝶阀7关到位", "system": "df7gdw", "address": "10028", "dataType": "bit"}, | ||
| 55 | + {"name": "蝶阀8开到位", "system": "df8kdw", "address": "10029", "dataType": "bit"}, | ||
| 56 | + {"name": "蝶阀8关到位", "system": "df8gdw", "address": "10030", "dataType": "bit"}, | ||
| 57 | + {"name": "循环水泵运行", "system": "xhsbyx", "address": "10031", "dataType": "bit"}, | ||
| 58 | + {"name": "系统报警", "system": "xtbj", "address": "00001", "dataType": "bit"}, | ||
| 59 | + {"name": "水泵1故障", "system": "sb1gz", "address": "40001.01", "dataType": "bit"}, | ||
| 60 | + {"name": "水泵2故障", "system": "sb2gz", "address": "40001.02", "dataType": "bit"}, | ||
| 61 | + {"name": "氧锥泵1故障", "system": "yzb1gz", "address": "40001.03", "dataType": "bit"}, | ||
| 62 | + {"name": "氧锥泵2故障", "system": "yzb2gz", "address": "40001.04", "dataType": "bit"}, | ||
| 63 | + {"name": "氧锥泵3故障", "system": "yzb3gz", "address": "40001.05", "dataType": "bit"}, | ||
| 64 | + {"name": "氧锥泵4故障", "system": "yzb4gz", "address": "40001.06", "dataType": "bit"}, | ||
| 65 | + {"name": "排污泵故障", "system": "pwb_gz", "address": "40001.07", "dataType": "bit"}, | ||
| 66 | + {"name": "排污阀1开不到位", "system": "pwf1kbdw", "address": "40001.09", "dataType": "bit"}, | ||
| 67 | + {"name": "排污阀1关不到位", "system": "pwf1gbdw", "address": "40001.10", "dataType": "bit"}, | ||
| 68 | + {"name": "排污阀2开不到位", "system": "pwf2kbdw", "address": "40001.11", "dataType": "bit"}, | ||
| 69 | + {"name": "排污阀2关不到位", "system": "pwf2gbdw", "address": "40001.12", "dataType": "bit"}, | ||
| 70 | + {"name": "排污阀3开不到位", "system": "pwf3kbdw", "address": "40001.13", "dataType": "bit"}, | ||
| 71 | + {"name": "排污阀3关不到位", "system": "pwf3gbdw", "address": "40001.14", "dataType": "bit"}, | ||
| 72 | + {"name": "排污阀4开不到位", "system": "pwf4kbdw", "address": "40001.15", "dataType": "bit"}, | ||
| 73 | + {"name": "排污阀4关不到位", "system": "pwf4gbdw", "address": "40001.16", "dataType": "bit"}, | ||
| 74 | + {"name": "排污阀5开不到位", "system": "pwf5kbdw", "address": "40002.01", "dataType": "bit"}, | ||
| 75 | + {"name": "排污阀5关不到位", "system": "pwf5gbdw", "address": "40002.02", "dataType": "bit"}, | ||
| 76 | + {"name": "排污阀6开不到位", "system": "pwf6kbdw", "address": "40002.03", "dataType": "bit"}, | ||
| 77 | + {"name": "排污阀6关不到位", "system": "pwf6gbdw", "address": "40002.04", "dataType": "bit"}, | ||
| 78 | + {"name": "排污阀7开不到位", "system": "pwf7kbdw", "address": "40002.05", "dataType": "bit"}, | ||
| 79 | + {"name": "排污阀7关不到位", "system": "pwf7gbdw", "address": "40002.06", "dataType": "bit"}, | ||
| 80 | + {"name": "排污阀8开不到位", "system": "pwf8kbdw", "address": "40002.07", "dataType": "bit"}, | ||
| 81 | + {"name": "排污阀8关不到位", "system": "pwf8gbdw", "address": "40002.08", "dataType": "bit"}, | ||
| 82 | + {"name": "补水高液位超时", "system": "bsgywdcs", "address": "40002.11", "dataType": "bit"}, | ||
| 83 | + {"name": "微滤池高液位超时", "system": "wlcgywdcs", "address": "40002.12", "dataType": "bit"}, | ||
| 84 | + {"name": "微滤机电源跳闸", "system": "wljdytz", "address": "40002.13", "dataType": "bit"}, | ||
| 85 | + {"name": "紫外杀菌灯跳闸", "system": "zwsjd_tz", "address": "40002.14", "dataType": "bit"}, | ||
| 86 | + {"name": "溶氧超限报警", "system": "rycxbj", "address": "40002.15", "dataType": "bit"}, | ||
| 87 | + {"name": "微滤池低液位长时间不消失报警", "system": "wlcdywbcsbj", "address": "40002.16", "dataType": "bit"}, | ||
| 88 | + {"name": "溶氧值", "system": "ry", "address": "40003-40004", "dataType": "float32"}, | ||
| 89 | + {"name": "温度值", "system": "wd", "address": "40005-40006", "dataType": "float32"}, | ||
| 90 | + {"name": "电能值", "system": "dn", "address": "40007-40008", "dataType": "float32"}, | ||
| 91 | + {"name": "当前氧锥泵运行台数", "system": "dqyzb", "address": "40009", "dataType": "int16"}, | ||
| 92 | + {"name": "氧锥泵1运行时间", "system": "yzb1_sj", "address": "40011-40012", "dataType": "int32"}, | ||
| 93 | + {"name": "氧锥泵2运行时间", "system": "yzb2_sj", "address": "40013-40014", "dataType": "int32"}, | ||
| 94 | + {"name": "氧锥泵3运行时间", "system": "yzb3_sj", "address": "40015-40016", "dataType": "int32"}, | ||
| 95 | + {"name": "氧锥泵4运行时间", "system": "yzb4_sj", "address": "40017-40018", "dataType": "int32"}, | ||
| 96 | + {"name": "生化池水温", "system": "shcsw", "address": "40019-40020", "dataType": "float32"}, | ||
| 97 | + {"name": "循环水泵故障", "system": "xhsb_gz", "address": "40021.01", "dataType": "bit"}, | ||
| 98 | + {"name": "生化池水温低限报警", "system": "shcsw_dx_bj", "address": "40021.02", "dataType": "bit"}, | ||
| 99 | + {"name": "生化池水温高限报警", "system": "shcsw_gx_bj", "address": "40021.03", "dataType": "bit"}, | ||
| 100 | + {"name": "排污阀1开OR关", "system": "pwf1_or", "address": "40051.01", "dataType": "bit"}, | ||
| 101 | + {"name": "排污阀2开OR关", "system": "pwf2_or", "address": "40051.02", "dataType": "bit"}, | ||
| 102 | + {"name": "排污阀3开OR关", "system": "pwf3_or", "address": "40051.03", "dataType": "bit"}, | ||
| 103 | + {"name": "排污阀4开OR关", "system": "pwf4_or", "address": "40051.04", "dataType": "bit"}, | ||
| 104 | + {"name": "排污阀5开OR关", "system": "pwf5_or", "address": "40051.05", "dataType": "bit"}, | ||
| 105 | + {"name": "排污阀6开OR关", "system": "pwf6_or", "address": "40051.06", "dataType": "bit"}, | ||
| 106 | + {"name": "排污阀7开OR关", "system": "pwf7_or", "address": "40051.07", "dataType": "bit"}, | ||
| 107 | + {"name": "排污阀8开OR关", "system": "pwf8_or", "address": "40051.08", "dataType": "bit"}, | ||
| 108 | + {"name": "水泵1启动", "system": "sb1_qd", "address": "40051.09", "dataType": "bit"}, | ||
| 109 | + {"name": "水泵2启动", "system": "sb2_qd", "address": "40051.10", "dataType": "bit"}, | ||
| 110 | + {"name": "氧锥泵1启动", "system": "yzb1_qd", "address": "40051.11", "dataType": "bit"}, | ||
| 111 | + {"name": "氧锥泵2启动", "system": "yzb2_qd", "address": "40051.12", "dataType": "bit"}, | ||
| 112 | + {"name": "氧锥泵3启动", "system": "yzb3_qd", "address": "40051.13", "dataType": "bit"}, | ||
| 113 | + {"name": "氧锥泵4启动", "system": "yzb4_qd", "address": "40051.14", "dataType": "bit"}, | ||
| 114 | + {"name": "排污泵启动", "system": "pwb_qd", "address": "40051.15", "dataType": "bit"}, | ||
| 115 | + {"name": "水泵1停止", "system": "sb1_tz", "address": "40052.01", "dataType": "bit"}, | ||
| 116 | + {"name": "水泵2停止", "system": "sb2_tz", "address": "40052.02", "dataType": "bit"}, | ||
| 117 | + {"name": "氧锥泵1停止", "system": "yzb1_tz", "address": "40052.03", "dataType": "bit"}, | ||
| 118 | + {"name": "氧锥泵2停止", "system": "yzb2_tz", "address": "40052.04", "dataType": "bit"}, | ||
| 119 | + {"name": "氧锥泵3停止", "system": "yzb3_tz", "address": "40052.05", "dataType": "bit"}, | ||
| 120 | + {"name": "氧锥泵4停止", "system": "yzb4_tz", "address": "40052.06", "dataType": "bit"}, | ||
| 121 | + {"name": "排污泵停止", "system": "pwb_tz", "address": "40052.07", "dataType": "bit"}, | ||
| 122 | + {"name": "清报警", "system": "qbj", "address": "40052.09", "dataType": "bit"}, | ||
| 123 | + {"name": "累计时间清零", "system": "lj_sjql", "address": "40052.10", "dataType": "bit"}, | ||
| 124 | + {"name": "溶氧上限报警设定值", "system": "ry_sxsz", "address": "40053-40054", "dataType": "float32"}, | ||
| 125 | + {"name": "溶氧下限报警设定值", "system": "ry_xxsz", "address": "40055-40056", "dataType": "float32"} | ||
| 126 | + ] | ||
| 127 | + }, | ||
| 128 | + { | ||
| 129 | + "id": 2, | ||
| 130 | + "systemName": "成鱼系统2", | ||
| 131 | + "protocolType": "TCP", | ||
| 132 | + "connectConfig": { "host": "192.168.2.2", "port": 2001}, | ||
| 133 | + "points": [ | ||
| 134 | + {"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"}, | ||
| 135 | + {"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"}, | ||
| 136 | + {"name": "补水泵启动", "system": "bsbqd", "address": "10003", "dataType": "bit"}, | ||
| 137 | + {"name": "水泵1运行", "system": "sb1yx", "address": "10004", "dataType": "bit"}, | ||
| 138 | + {"name": "水泵2运行", "system": "sb2yx", "address": "10005", "dataType": "bit"}, | ||
| 139 | + {"name": "氧锥泵1运行", "system": "yzb1yx", "address": "10006", "dataType": "bit"}, | ||
| 140 | + {"name": "氧锥泵2运行", "system": "yzb2yx", "address": "10007", "dataType": "bit"}, | ||
| 141 | + {"name": "氧锥泵3运行", "system": "yzb3yx", "address": "10008", "dataType": "bit"}, | ||
| 142 | + {"name": "氧锥泵4运行", "system": "yzb4yx", "address": "10009", "dataType": "bit"}, | ||
| 143 | + {"name": "排污泵运行", "system": "pwb", "address": "10010", "dataType": "bit"}, | ||
| 144 | + {"name": "微滤机电源合闸", "system": "wljdyhz", "address": "10011", "dataType": "bit"}, | ||
| 145 | + {"name": "紫外灯电源合闸", "system": "zwd", "address": "10012", "dataType": "bit"}, | ||
| 146 | + {"name": "微滤池液位高", "system": "wlcyw_g", "address": "10013", "dataType": "bit"}, | ||
| 147 | + {"name": "微滤池液位低", "system": "wlcyw_d", "address": "10014", "dataType": "bit"}, | ||
| 148 | + {"name": "蝶阀1开到位", "system": "df1kdw", "address": "10015", "dataType": "bit"}, | ||
| 149 | + {"name": "蝶阀1关到位", "system": "df1gdw", "address": "10016", "dataType": "bit"}, | ||
| 150 | + {"name": "蝶阀2开到位", "system": "df2kdw", "address": "10017", "dataType": "bit"}, | ||
| 151 | + {"name": "蝶阀2关到位", "system": "df2gdw", "address": "10018", "dataType": "bit"}, | ||
| 152 | + {"name": "蝶阀3开到位", "system": "df3kdw", "address": "10019", "dataType": "bit"}, | ||
| 153 | + {"name": "蝶阀3关到位", "system": "df3gdw", "address": "10020", "dataType": "bit"}, | ||
| 154 | + {"name": "蝶阀4开到位", "system": "df4kdw", "address": "10021", "dataType": "bit"}, | ||
| 155 | + {"name": "蝶阀4关到位", "system": "df4gdw", "address": "10022", "dataType": "bit"}, | ||
| 156 | + {"name": "蝶阀5开到位", "system": "df5kdw", "address": "10023", "dataType": "bit"}, | ||
| 157 | + {"name": "蝶阀5关到位", "system": "df5gdw", "address": "10024", "dataType": "bit"}, | ||
| 158 | + {"name": "蝶阀6开到位", "system": "df6kdw", "address": "10025", "dataType": "bit"}, | ||
| 159 | + {"name": "蝶阀6关到位", "system": "df6gdw", "address": "10026", "dataType": "bit"}, | ||
| 160 | + {"name": "蝶阀7开到位", "system": "df7kdw", "address": "10027", "dataType": "bit"}, | ||
| 161 | + {"name": "蝶阀7关到位", "system": "df7gdw", "address": "10028", "dataType": "bit"}, | ||
| 162 | + {"name": "蝶阀8开到位", "system": "df8kdw", "address": "10029", "dataType": "bit"}, | ||
| 163 | + {"name": "蝶阀8关到位", "system": "df8gdw", "address": "10030", "dataType": "bit"}, | ||
| 164 | + {"name": "循环水泵运行", "system": "xhsbyx", "address": "10031", "dataType": "bit"}, | ||
| 165 | + {"name": "系统报警", "system": "xtbj", "address": "00001", "dataType": "bit"}, | ||
| 166 | + {"name": "水泵1故障", "system": "sb1gz", "address": "40001.01", "dataType": "bit"}, | ||
| 167 | + {"name": "水泵2故障", "system": "sb2gz", "address": "40001.02", "dataType": "bit"}, | ||
| 168 | + {"name": "氧锥泵1故障", "system": "yzb1gz", "address": "40001.03", "dataType": "bit"}, | ||
| 169 | + {"name": "氧锥泵2故障", "system": "yzb2gz", "address": "40001.04", "dataType": "bit"}, | ||
| 170 | + {"name": "氧锥泵3故障", "system": "yzb3gz", "address": "40001.05", "dataType": "bit"}, | ||
| 171 | + {"name": "氧锥泵4故障", "system": "yzb4gz", "address": "40001.06", "dataType": "bit"}, | ||
| 172 | + {"name": "排污泵故障", "system": "pwb_gz", "address": "40001.07", "dataType": "bit"}, | ||
| 173 | + {"name": "排污阀1开不到位", "system": "pwf1kbdw", "address": "40001.09", "dataType": "bit"}, | ||
| 174 | + {"name": "排污阀1关不到位", "system": "pwf1gbdw", "address": "40001.10", "dataType": "bit"}, | ||
| 175 | + {"name": "排污阀2开不到位", "system": "pwf2kbdw", "address": "40001.11", "dataType": "bit"}, | ||
| 176 | + {"name": "排污阀2关不到位", "system": "pwf2gbdw", "address": "40001.12", "dataType": "bit"}, | ||
| 177 | + {"name": "排污阀3开不到位", "system": "pwf3kbdw", "address": "40001.13", "dataType": "bit"}, | ||
| 178 | + {"name": "排污阀3关不到位", "system": "pwf3gbdw", "address": "40001.14", "dataType": "bit"}, | ||
| 179 | + {"name": "排污阀4开不到位", "system": "pwf4kbdw", "address": "40001.15", "dataType": "bit"}, | ||
| 180 | + {"name": "排污阀4关不到位", "system": "pwf4gbdw", "address": "40001.16", "dataType": "bit"}, | ||
| 181 | + {"name": "排污阀5开不到位", "system": "pwf5kbdw", "address": "40002.01", "dataType": "bit"}, | ||
| 182 | + {"name": "排污阀5关不到位", "system": "pwf5gbdw", "address": "40002.02", "dataType": "bit"}, | ||
| 183 | + {"name": "排污阀6开不到位", "system": "pwf6kbdw", "address": "40002.03", "dataType": "bit"}, | ||
| 184 | + {"name": "排污阀6关不到位", "system": "pwf6gbdw", "address": "40002.04", "dataType": "bit"}, | ||
| 185 | + {"name": "排污阀7开不到位", "system": "pwf7kbdw", "address": "40002.05", "dataType": "bit"}, | ||
| 186 | + {"name": "排污阀7关不到位", "system": "pwf7gbdw", "address": "40002.06", "dataType": "bit"}, | ||
| 187 | + {"name": "排污阀8开不到位", "system": "pwf8kbdw", "address": "40002.07", "dataType": "bit"}, | ||
| 188 | + {"name": "排污阀8关不到位", "system": "pwf8gbdw", "address": "40002.08", "dataType": "bit"}, | ||
| 189 | + {"name": "补水高液位超时", "system": "bsgywdcs", "address": "40002.11", "dataType": "bit"}, | ||
| 190 | + {"name": "微滤池高液位超时", "system": "wlcgywdcs", "address": "40002.12", "dataType": "bit"}, | ||
| 191 | + {"name": "微滤机电源跳闸", "system": "wljdytz", "address": "40002.13", "dataType": "bit"}, | ||
| 192 | + {"name": "紫外杀菌灯跳闸", "system": "zwsjd_tz", "address": "40002.14", "dataType": "bit"}, | ||
| 193 | + {"name": "溶氧超限报警", "system": "rycxbj", "address": "40002.15", "dataType": "bit"}, | ||
| 194 | + {"name": "微滤池低液位长时间不消失报警", "system": "wlcdywbcsbj", "address": "40002.16", "dataType": "bit"}, | ||
| 195 | + {"name": "溶氧值", "system": "ry", "address": "40003-40004", "dataType": "float32"}, | ||
| 196 | + {"name": "温度值", "system": "wd", "address": "40005-40006", "dataType": "float32"}, | ||
| 197 | + {"name": "电能值", "system": "dn", "address": "40007-40008", "dataType": "float32"}, | ||
| 198 | + {"name": "当前氧锥泵运行台数", "system": "dqyzb", "address": "40009", "dataType": "int16"}, | ||
| 199 | + {"name": "氧锥泵1运行时间", "system": "yzb1_sj", "address": "40011-40012", "dataType": "int32"}, | ||
| 200 | + {"name": "氧锥泵2运行时间", "system": "yzb2_sj", "address": "40013-40014", "dataType": "int32"}, | ||
| 201 | + {"name": "氧锥泵3运行时间", "system": "yzb3_sj", "address": "40015-40016", "dataType": "int32"}, | ||
| 202 | + {"name": "氧锥泵4运行时间", "system": "yzb4_sj", "address": "40017-40018", "dataType": "int32"}, | ||
| 203 | + {"name": "生化池水温", "system": "shcsw", "address": "40019-40020", "dataType": "float32"}, | ||
| 204 | + {"name": "循环水泵故障", "system": "xhsb_gz", "address": "40021.01", "dataType": "bit"}, | ||
| 205 | + {"name": "生化池水温低限报警", "system": "shcsw_dx_bj", "address": "40021.02", "dataType": "bit"}, | ||
| 206 | + {"name": "生化池水温高限报警", "system": "shcsw_gx_bj", "address": "40021.03", "dataType": "bit"}, | ||
| 207 | + {"name": "排污阀1开OR关", "system": "pwf1_or", "address": "40051.01", "dataType": "bit"}, | ||
| 208 | + {"name": "排污阀2开OR关", "system": "pwf2_or", "address": "40051.02", "dataType": "bit"}, | ||
| 209 | + {"name": "排污阀3开OR关", "system": "pwf3_or", "address": "40051.03", "dataType": "bit"}, | ||
| 210 | + {"name": "排污阀4开OR关", "system": "pwf4_or", "address": "40051.04", "dataType": "bit"}, | ||
| 211 | + {"name": "排污阀5开OR关", "system": "pwf5_or", "address": "40051.05", "dataType": "bit"}, | ||
| 212 | + {"name": "排污阀6开OR关", "system": "pwf6_or", "address": "40051.06", "dataType": "bit"}, | ||
| 213 | + {"name": "排污阀7开OR关", "system": "pwf7_or", "address": "40051.07", "dataType": "bit"}, | ||
| 214 | + {"name": "排污阀8开OR关", "system": "pwf8_or", "address": "40051.08", "dataType": "bit"}, | ||
| 215 | + {"name": "水泵1启动", "system": "sb1_qd", "address": "40051.09", "dataType": "bit"}, | ||
| 216 | + {"name": "水泵2启动", "system": "sb2_qd", "address": "40051.10", "dataType": "bit"}, | ||
| 217 | + {"name": "氧锥泵1启动", "system": "yzb1_qd", "address": "40051.11", "dataType": "bit"}, | ||
| 218 | + {"name": "氧锥泵2启动", "system": "yzb2_qd", "address": "40051.12", "dataType": "bit"}, | ||
| 219 | + {"name": "氧锥泵3启动", "system": "yzb3_qd", "address": "40051.13", "dataType": "bit"}, | ||
| 220 | + {"name": "氧锥泵4启动", "system": "yzb4_qd", "address": "40051.14", "dataType": "bit"}, | ||
| 221 | + {"name": "排污泵启动", "system": "pwb_qd", "address": "40051.15", "dataType": "bit"}, | ||
| 222 | + {"name": "水泵1停止", "system": "sb1_tz", "address": "40052.01", "dataType": "bit"}, | ||
| 223 | + {"name": "水泵2停止", "system": "sb2_tz", "address": "40052.02", "dataType": "bit"}, | ||
| 224 | + {"name": "氧锥泵1停止", "system": "yzb1_tz", "address": "40052.03", "dataType": "bit"}, | ||
| 225 | + {"name": "氧锥泵2停止", "system": "yzb2_tz", "address": "40052.04", "dataType": "bit"}, | ||
| 226 | + {"name": "氧锥泵3停止", "system": "yzb3_tz", "address": "40052.05", "dataType": "bit"}, | ||
| 227 | + {"name": "氧锥泵4停止", "system": "yzb4_tz", "address": "40052.06", "dataType": "bit"}, | ||
| 228 | + {"name": "排污泵停止", "system": "pwb_tz", "address": "40052.07", "dataType": "bit"}, | ||
| 229 | + {"name": "清报警", "system": "qbj", "address": "40052.09", "dataType": "bit"}, | ||
| 230 | + {"name": "累计时间清零", "system": "lj_sjql", "address": "40052.10", "dataType": "bit"}, | ||
| 231 | + {"name": "溶氧上限报警设定值", "system": "ry_sxsz", "address": "40053-40054", "dataType": "float32"}, | ||
| 232 | + {"name": "溶氧下限报警设定值", "system": "ry_xxsz", "address": "40055-40056", "dataType": "float32"} | ||
| 233 | + ] | ||
| 234 | + }, | ||
| 235 | + { | ||
| 236 | + "id": 3, | ||
| 237 | + "systemName": "源水处理区", | ||
| 238 | + "protocolType": "TCP", | ||
| 239 | + "connectConfig": { "host": "192.168.2.5", "port": 2004}, | ||
| 240 | + "points": [ | ||
| 241 | + {"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"}, | ||
| 242 | + {"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"}, | ||
| 243 | + {"name": "水源泵1启动", "system": "syp1", "address": "10003", "dataType": "bit"}, | ||
| 244 | + {"name": "水源泵2启动", "system": "syp2", "address": "10004", "dataType": "bit"}, | ||
| 245 | + {"name": "水源泵3启动", "system": "syp3", "address": "10005", "dataType": "bit"}, | ||
| 246 | + {"name": "风机1启动", "system": "fj1", "address": "10006", "dataType": "bit"}, | ||
| 247 | + {"name": "风机2启动", "system": "fj2", "address": "10007", "dataType": "bit"}, | ||
| 248 | + {"name": "紫外灯电源合闸", "system": "zw", "address": "10008", "dataType": "bit"}, | ||
| 249 | + {"name": "生化池高液位", "system": "shg", "address": "10009", "dataType": "bit"}, | ||
| 250 | + {"name": "生化池低液位", "system": "shd", "address": "10010", "dataType": "bit"}, | ||
| 251 | + {"name": "水源泵1故障", "system": "syp1g", "address": "40001.01", "dataType": "bit"}, | ||
| 252 | + {"name": "水源泵2故障", "system": "syp2g", "address": "40001.02", "dataType": "bit"}, | ||
| 253 | + {"name": "水源泵3故障", "system": "syp3g", "address": "40001.03", "dataType": "bit"}, | ||
| 254 | + {"name": "风机1故障", "system": "fj1g", "address": "40001.04", "dataType": "bit"}, | ||
| 255 | + {"name": "风机2故障", "system": "fj2g", "address": "40001.05", "dataType": "bit"}, | ||
| 256 | + {"name": "紫外杀菌等跳闸故障", "system": "zwg", "address": "40001.06", "dataType": "bit"}, | ||
| 257 | + {"name": "电能值", "system": "dn", "address": "40007-40008", "dataType": "float"}, | ||
| 258 | + {"name": "当前水源泵启动台数", "system": "dqsy", "address": "40009", "dataType": "int"}, | ||
| 259 | + {"name": "当前风机运行台数", "system": "dqfj", "address": "40010", "dataType": "int"}, | ||
| 260 | + {"name": "水源泵1启动时间", "system": "syp1sj", "address": "40011-40012", "dataType": "long"}, | ||
| 261 | + {"name": "水源泵2启动时间", "system": "syp2sj", "address": "40013-40014", "dataType": "long"}, | ||
| 262 | + {"name": "水源泵3启动时间", "system": "syp3sj", "address": "40015-40016", "dataType": "long"}, | ||
| 263 | + {"name": "风机1启动时间", "system": "fj1sj", "address": "40017-40018", "dataType": "long"}, | ||
| 264 | + {"name": "风机2启动时间", "system": "fj2sj", "address": "40019-40020", "dataType": "long"}, | ||
| 265 | + {"name": "补水阀1开OR关", "system": "bsf1", "address": "40051.01", "dataType": "bit"}, | ||
| 266 | + {"name": "补水阀2开OR关", "system": "bsf2", "address": "40051.02", "dataType": "bit"}, | ||
| 267 | + {"name": "水源泵1启动", "system": "syp1s", "address": "40051.01", "dataType": "bit"}, | ||
| 268 | + {"name": "水源泵2启动", "system": "syp2s", "address": "40051.02", "dataType": "bit"}, | ||
| 269 | + {"name": "水源泵3启动", "system": "syp3s", "address": "40051.03", "dataType": "bit"}, | ||
| 270 | + {"name": "风机1启动", "system": "fj1s", "address": "40051.04", "dataType": "bit"}, | ||
| 271 | + {"name": "风机2启动", "system": "fj2s", "address": "40051.05", "dataType": "bit"}, | ||
| 272 | + {"name": "水源泵1停止", "system": "syp1t", "address": "40051.09", "dataType": "bit"}, | ||
| 273 | + {"name": "水源泵2停止", "system": "syp2t", "address": "40051.10", "dataType": "bit"}, | ||
| 274 | + {"name": "水源泵3停止", "system": "syp3t", "address": "40051.11", "dataType": "bit"}, | ||
| 275 | + {"name": "风机1停止", "system": "fj1t", "address": "40051.12", "dataType": "bit"}, | ||
| 276 | + {"name": "风机2停止", "system": "fj2t", "address": "40051.13", "dataType": "bit"}, | ||
| 277 | + {"name": "清报警", "system": "qbj", "address": "40052.01", "dataType": "bit"}, | ||
| 278 | + {"name": "累计时间清零", "system": "ljsj", "address": "40052.02", "dataType": "bit"} | ||
| 279 | + ] | ||
| 280 | + }, | ||
| 281 | + { | ||
| 282 | + "id": 4, | ||
| 283 | + "systemName": "育苗系统", | ||
| 284 | + "protocolType": "TCP", | ||
| 285 | + "connectConfig": { "host": "192.168.2.4", "port": 2002}, | ||
| 286 | + "points": [ | ||
| 287 | + {"name": "手动/自动", "system": "sdz", "address": "10001", "dataType": "bit"}, | ||
| 288 | + {"name": "本地/远程", "system": "bdyy", "address": "10002", "dataType": "bit"}, | ||
| 289 | + {"name": "水泵1运行", "system": "sb1", "address": "10004", "dataType": "bit"}, | ||
| 290 | + {"name": "水泵2运行", "system": "sb2", "address": "10005", "dataType": "bit"}, | ||
| 291 | + {"name": "风机1运行", "system": "fj1", "address": "10006", "dataType": "bit"}, | ||
| 292 | + {"name": "风机2运行", "system": "fj2", "address": "10007", "dataType": "bit"}, | ||
| 293 | + {"name": "热源泵1电源合闸", "system": "ryb1", "address": "10008", "dataType": "bit"}, | ||
| 294 | + {"name": "热源泵2电源合闸", "system": "ryb2", "address": "10009", "dataType": "bit"}, | ||
| 295 | + {"name": "微滤机电源合闸", "system": "wlj", "address": "10010", "dataType": "bit"}, | ||
| 296 | + {"name": "紫外灯电源合闸", "system": "zwd", "address": "10011", "dataType": "bit"}, | ||
| 297 | + {"name": "补水池高液位", "system": "bsc", "address": "10012", "dataType": "bit"}, | ||
| 298 | + {"name": "微滤池高液位", "system": "wlq", "address": "10013", "dataType": "bit"}, | ||
| 299 | + {"name": "溶氧超限报警", "system": "rycj", "address": "10015", "dataType": "bit"}, | ||
| 300 | + {"name": "微滤池低液位", "system": "wld", "address": "10015", "dataType": "bit"}, | ||
| 301 | + {"name": "微滤池低液位长时间不消失报警", "system": "wldc", "address": "10016", "dataType": "bit"}, | ||
| 302 | + {"name": "系统报警", "system": "xtbj", "address": "00001", "dataType": "bit"}, | ||
| 303 | + | ||
| 304 | + {"name": "溶氧值", "system": "ryz", "address": "40003-40004", "dataType": "float32"}, | ||
| 305 | + {"name": "温度值", "system": "wdz", "address": "40005-40006", "dataType": "float32"}, | ||
| 306 | + {"name": "电能值", "system": "dnz", "address": "40007-40008", "dataType": "float32"}, | ||
| 307 | + {"name": "当前风机运行台数", "system": "dqfj", "address": "40009", "dataType": "int32"}, | ||
| 308 | + {"name": "风机1运行时间", "system": "fj1sj", "address": "40011-40012", "dataType": "int64"}, | ||
| 309 | + {"name": "风机2运行时间", "system": "fj2sj", "address": "40013-40014", "dataType": "int64"}, | ||
| 310 | + | ||
| 311 | + {"name": "水泵1启动", "system": "sb1start", "address": "40051.01", "dataType": "bit"}, | ||
| 312 | + {"name": "水泵2启动", "system": "sb2start", "address": "40051.02", "dataType": "bit"}, | ||
| 313 | + {"name": "风机1启动", "system": "fj1start", "address": "40051.03", "dataType": "bit"}, | ||
| 314 | + {"name": "风机2启动", "system": "fj2start", "address": "40051.04", "dataType": "bit"}, | ||
| 315 | + {"name": "补水泵3启动", "system": "bsp3start", "address": "40051.05", "dataType": "bit"}, | ||
| 316 | + {"name": "水泵1停止", "system": "sb1stop", "address": "40051.09", "dataType": "bit"}, | ||
| 317 | + {"name": "水泵2停止", "system": "sb2stop", "address": "40051.10", "dataType": "bit"}, | ||
| 318 | + {"name": "风机1停止", "system": "fj1stop", "address": "40051.11", "dataType": "bit"}, | ||
| 319 | + {"name": "风机2停止", "system": "fj2stop", "address": "40051.12", "dataType": "bit"}, | ||
| 320 | + {"name": "补水泵3停止", "system": "bsp3stop", "address": "40051.13", "dataType": "bit"}, | ||
| 321 | + | ||
| 322 | + {"name": "清报警", "system": "qbj", "address": "40052.01", "dataType": "bit"}, | ||
| 323 | + {"name": "累计时间清零", "system": "ljsjql", "address": "40052.02", "dataType": "bit"}, | ||
| 324 | + | ||
| 325 | + {"name": "溶氧上限报警设定值", "system": "rysjup", "address": "40053-40054", "dataType": "float32"}, | ||
| 326 | + {"name": "溶氧下限报警设定值", "system": "rysjdown", "address": "40055-40056", "dataType": "float32"} | ||
| 327 | + ] | ||
| 328 | + }, | ||
| 329 | + { | ||
| 330 | + "id": 5, | ||
| 331 | + "systemName": "设备房系统", | ||
| 332 | + "protocolType": "TCP", | ||
| 333 | + "connectConfig": { "host": "192.168.2.3", "port": 2003}, | ||
| 334 | + "points": [ | ||
| 335 | + {"name": "自动", "system": "zd", "address": "10001", "dataType": "bit"}, | ||
| 336 | + {"name": "远程", "system": "yc", "address": "10002", "dataType": "bit"}, | ||
| 337 | + {"name": "风机1运行", "system": "fj1", "address": "10003", "dataType": "bit"}, | ||
| 338 | + {"name": "风机2运行", "system": "fj2", "address": "10004", "dataType": "bit"}, | ||
| 339 | + {"name": "风机3运行", "system": "fj3", "address": "10005", "dataType": "bit"}, | ||
| 340 | + {"name": "风机4运行", "system": "fj4", "address": "10006", "dataType": "bit"}, | ||
| 341 | + {"name": "补水泵1运行", "system": "bsb1", "address": "10007", "dataType": "bit"}, | ||
| 342 | + {"name": "补水泵2运行", "system": "bsb2", "address": "10008", "dataType": "bit"}, | ||
| 343 | + {"name": "热泵1电源合闸", "system": "rb1", "address": "10010", "dataType": "bit"}, | ||
| 344 | + {"name": "热泵2电源合闸", "system": "rb2", "address": "10011", "dataType": "bit"}, | ||
| 345 | + {"name": "空压机电源合闸", "system": "kyj", "address": "10012", "dataType": "bit"}, | ||
| 346 | + {"name": "补水阀1开到位", "system": "bsf1", "address": "10013", "dataType": "bit"}, | ||
| 347 | + {"name": "补水阀2开到位", "system": "bsf2", "address": "10014", "dataType": "bit"}, | ||
| 348 | + {"name": "补水阀1关到位", "system": "bsf1g", "address": "10016", "dataType": "bit"}, | ||
| 349 | + {"name": "补水阀2关到位", "system": "bsf2g", "address": "10017", "dataType": "bit"}, | ||
| 350 | + {"name": "补水1高液位", "system": "bsg1", "address": "10019", "dataType": "bit"}, | ||
| 351 | + {"name": "补水2高液位", "system": "bsg2", "address": "10020", "dataType": "bit"}, | ||
| 352 | + {"name": "系统报警", "system": "bj", "address": "00001", "dataType": "bit"}, | ||
| 353 | + | ||
| 354 | + {"name": "风机1故障", "system": "fj1g", "address": "40001.01", "dataType": "bit"}, | ||
| 355 | + {"name": "风机2故障", "system": "fj2g", "address": "40001.02", "dataType": "bit"}, | ||
| 356 | + {"name": "风机3故障", "system": "fj3g", "address": "40001.03", "dataType": "bit"}, | ||
| 357 | + {"name": "风机4故障", "system": "fj4g", "address": "40001.04", "dataType": "bit"}, | ||
| 358 | + {"name": "补水泵1故障", "system": "bsb1g", "address": "40001.05", "dataType": "bit"}, | ||
| 359 | + {"name": "补水泵2故障", "system": "bsb2g", "address": "40001.06", "dataType": "bit"}, | ||
| 360 | + {"name": "热泵1跳闸故障", "system": "rb1g", "address": "40001.08", "dataType": "bit"}, | ||
| 361 | + {"name": "热泵2跳闸故障", "system": "rb2g", "address": "40001.09", "dataType": "bit"}, | ||
| 362 | + {"name": "补水阀1开不到位", "system": "bsf1b", "address": "40001.10", "dataType": "bit"}, | ||
| 363 | + {"name": "补水阀1关不到位", "system": "bsf1bg", "address": "40001.11", "dataType": "bit"}, | ||
| 364 | + {"name": "补水阀2开不到位", "system": "bsf2b", "address": "40001.12", "dataType": "bit"}, | ||
| 365 | + {"name": "补水阀2关不到位", "system": "bsf2bg", "address": "40001.13", "dataType": "bit"}, | ||
| 366 | + {"name": "空压机跳闸故障", "system": "kyjg", "address": "40001.16", "dataType": "bit"}, | ||
| 367 | + | ||
| 368 | + {"name": "电能值", "system": "dnz", "address": "40007-40008", "dataType": "float"}, | ||
| 369 | + {"name": "当前风机运行台数", "system": "dqfj", "address": "40009", "dataType": "int"}, | ||
| 370 | + {"name": "风机1运行时间", "system": "fj1t", "address": "40011-40012", "dataType": "long"}, | ||
| 371 | + {"name": "风机2运行时间", "system": "fj2t", "address": "40013-40014", "dataType": "long"}, | ||
| 372 | + {"name": "风机3运行时间", "system": "fj3t", "address": "40015-40016", "dataType": "long"}, | ||
| 373 | + {"name": "风机4运行时间", "system": "fj4t", "address": "40017-40018", "dataType": "long"}, | ||
| 374 | + | ||
| 375 | + {"name": "补水阀1开OR关", "system": "bsf1c", "address": "40051.01", "dataType": "bit"}, | ||
| 376 | + {"name": "补水阀2开OR关", "system": "bsf2c", "address": "40051.02", "dataType": "bit"}, | ||
| 377 | + {"name": "风机1启动", "system": "fj1s", "address": "40051.09", "dataType": "bit"}, | ||
| 378 | + {"name": "风机2启动", "system": "fj2s", "address": "40051.10", "dataType": "bit"}, | ||
| 379 | + {"name": "风机3启动", "system": "fj3s", "address": "40051.11", "dataType": "bit"}, | ||
| 380 | + {"name": "风机4启动", "system": "fj4s", "address": "40051.12", "dataType": "bit"}, | ||
| 381 | + {"name": "补水泵1启动", "system": "bsb1s", "address": "40051.13", "dataType": "bit"}, | ||
| 382 | + {"name": "补水泵2启动", "system": "bsb2s", "address": "40051.14", "dataType": "bit"}, | ||
| 383 | + {"name": "风机1停止", "system": "fj1p", "address": "40052.01", "dataType": "bit"}, | ||
| 384 | + {"name": "风机2停止", "system": "fj2p", "address": "40052.02", "dataType": "bit"}, | ||
| 385 | + {"name": "风机3停止", "system": "fj3p", "address": "40052.03", "dataType": "bit"}, | ||
| 386 | + {"name": "风机4停止", "system": "fj4p", "address": "40052.04", "dataType": "bit"}, | ||
| 387 | + {"name": "补水泵1停止", "system": "bsb1p", "address": "40052.05", "dataType": "bit"}, | ||
| 388 | + {"name": "补水泵2停止", "system": "bsb2p", "address": "40052.06", "dataType": "bit"}, | ||
| 389 | + {"name": "清报警", "system": "qbj", "address": "40052.09", "dataType": "bit"}, | ||
| 390 | + {"name": "累计时间清零", "system": "ljtq", "address": "40052.10", "dataType": "bit"} | ||
| 391 | + ] | ||
| 392 | + } | ||
| 393 | + ] | ||
| 394 | +} |
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | + | ||
| 3 | +<assembly> | ||
| 4 | + <id>bin</id> | ||
| 5 | + <!-- 最终打包成一个用于发布的zip文件 --> | ||
| 6 | + <formats> | ||
| 7 | + <format>zip</format> | ||
| 8 | + </formats> | ||
| 9 | + | ||
| 10 | + <!-- Adds dependencies to zip package under lib directory --> | ||
| 11 | + <dependencySets> | ||
| 12 | + <dependencySet> | ||
| 13 | + <!-- | ||
| 14 | + 不使用项目的artifact,第三方jar不要解压,打包进zip文件的lib目录 | ||
| 15 | + --> | ||
| 16 | + <useProjectArtifact>false</useProjectArtifact> | ||
| 17 | + <outputDirectory>lib</outputDirectory> | ||
| 18 | + <unpack>false</unpack> | ||
| 19 | + </dependencySet> | ||
| 20 | + </dependencySets> | ||
| 21 | + | ||
| 22 | + <fileSets> | ||
| 23 | + <!-- 把项目相关的说明文件,打包进zip文件的根目录 --> | ||
| 24 | + <fileSet> | ||
| 25 | + <directory>${project.basedir}</directory> | ||
| 26 | + <outputDirectory>/</outputDirectory> | ||
| 27 | + <includes> | ||
| 28 | + <include>README*</include> | ||
| 29 | + <include>LICENSE*</include> | ||
| 30 | + <include>NOTICE*</include> | ||
| 31 | + </includes> | ||
| 32 | + </fileSet> | ||
| 33 | + | ||
| 34 | + <!-- 把项目的配置文件,打包进zip文件的config目录 --> | ||
| 35 | + <fileSet> | ||
| 36 | + <directory>${project.basedir}\src\main\resources\configs</directory> | ||
| 37 | + <outputDirectory>../configs</outputDirectory> | ||
| 38 | + <includes> | ||
| 39 | + <include>*.properties</include> | ||
| 40 | + </includes> | ||
| 41 | + </fileSet> | ||
| 42 | + | ||
| 43 | + <!-- 把项目的配置文件,提出来 --> | ||
| 44 | + <fileSet> | ||
| 45 | + <directory>${project.basedir}\src\main\resources</directory> | ||
| 46 | + <outputDirectory>/</outputDirectory> | ||
| 47 | + <includes> | ||
| 48 | + <include>*.properties</include> | ||
| 49 | + <include>*.yml</include> | ||
| 50 | + </includes> | ||
| 51 | + </fileSet> | ||
| 52 | + | ||
| 53 | + <!-- 把项目的脚本文件目录( src/main/scripts )中的启动脚本文件,打包进zip文件的跟目录 --> | ||
| 54 | + <fileSet> | ||
| 55 | + <directory>${project.basedir}\bin</directory> | ||
| 56 | + <outputDirectory></outputDirectory> | ||
| 57 | + <includes> | ||
| 58 | + <include>start.*</include> | ||
| 59 | + <include>stop.*</include> | ||
| 60 | + </includes> | ||
| 61 | + </fileSet> | ||
| 62 | + | ||
| 63 | + <!-- 把项目自己编译出来的jar文件,打包进zip文件的根目录 --> | ||
| 64 | + <fileSet> | ||
| 65 | + <directory>${project.build.directory}</directory> | ||
| 66 | + <outputDirectory></outputDirectory> | ||
| 67 | + <includes> | ||
| 68 | + <include>*.jar</include> | ||
| 69 | + </includes> | ||
| 70 | + </fileSet> | ||
| 71 | + </fileSets> | ||
| 72 | +</assembly> |
| 1 | +import com.alibaba.fastjson.JSONObject; | ||
| 2 | +import com.zhonglai.luhui.device.modbus.terminal.config.InitPlcConfig; | ||
| 3 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jRead; | ||
| 4 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.Modbus4jWrite; | ||
| 5 | +import com.zhonglai.luhui.device.modbus.terminal.modbus.dto.PlcPoint; | ||
| 6 | + | ||
| 7 | +import java.util.ArrayList; | ||
| 8 | +import java.util.Arrays; | ||
| 9 | +import java.util.List; | ||
| 10 | +import java.util.Map; | ||
| 11 | + | ||
| 12 | +public class TestModbus { | ||
| 13 | + public static void main(String[] args) throws Exception { | ||
| 14 | + args = new String[]{"E:\\work\\idea\\Luhui\\lh-modules\\lh-device-modbus-terminal\\src\\main\\resources\\configs\\plcs.json","false","测试", "ry_sxsz=3.16","ry_xxsz=2.1"}; | ||
| 15 | + if (args.length < 2) { | ||
| 16 | + System.out.println("用法: java -jar modbus-app.jar <plcs.json路径> <点位名1> <点位名2> ..."); | ||
| 17 | + return; | ||
| 18 | + } | ||
| 19 | +// testRead(args); | ||
| 20 | + | ||
| 21 | + testWrite(args); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + private static void testRead(String[] args) throws Exception { | ||
| 25 | + String jsonPath = args[0]; | ||
| 26 | + boolean zeroBasedAddress = new Boolean(args[1]); | ||
| 27 | + Integer id = Integer.parseInt(args[2]); | ||
| 28 | + List<String> pointNames = Arrays.asList(Arrays.copyOfRange(args, 3, args.length)); | ||
| 29 | + | ||
| 30 | + InitPlcConfig.initPlcConfigFromFile(jsonPath); | ||
| 31 | + | ||
| 32 | + List<PlcPoint> plcPoints = new ArrayList<>(); | ||
| 33 | + for (String pointName :pointNames) | ||
| 34 | + { | ||
| 35 | + PlcPoint plcPoint = InitPlcConfig.getPlcSystem(id, pointName); | ||
| 36 | + if ( null != plcPoint) | ||
| 37 | + { | ||
| 38 | + plcPoints.add(plcPoint); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + } | ||
| 42 | + Map<String, Object> map = new Modbus4jRead(id).batchRead(plcPoints,zeroBasedAddress); | ||
| 43 | + System.out.println(JSONObject.toJSONString(map)); | ||
| 44 | + } | ||
| 45 | + private static void testWrite(String[] args) throws Exception | ||
| 46 | + { | ||
| 47 | + String jsonPath = args[0]; | ||
| 48 | + boolean zeroBasedAddress = new Boolean(args[1]); | ||
| 49 | + Integer id = Integer.parseInt(args[2]); | ||
| 50 | + List<String> pointNames = Arrays.asList(Arrays.copyOfRange(args, 3, args.length)); | ||
| 51 | + | ||
| 52 | + InitPlcConfig.initPlcConfigFromFile(jsonPath); | ||
| 53 | + | ||
| 54 | + List<PlcPoint> plcPoints = new ArrayList<>(); | ||
| 55 | + for (String pointName :pointNames) | ||
| 56 | + { | ||
| 57 | + String[] pointNameArr = pointName.split("="); | ||
| 58 | + PlcPoint plcPoint = InitPlcConfig.getPlcSystem(id, pointNameArr[0]); | ||
| 59 | + if ( null != plcPoint) | ||
| 60 | + { | ||
| 61 | + plcPoint.setValue(pointNameArr[1]); | ||
| 62 | + plcPoints.add(plcPoint); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + } | ||
| 66 | + new Modbus4jWrite(id).batchWrite(plcPoints,zeroBasedAddress); | ||
| 67 | + } | ||
| 68 | +} |
| @@ -62,6 +62,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | @@ -62,6 +62,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | ||
| 62 | return noticeMessageDomain; | 62 | return noticeMessageDomain; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | + @Override | ||
| 66 | + public NoticeMessageDto host(String deviceId, JsonObject jsonObject) { | ||
| 67 | + ParserDeviceHostDto parserDeviceHostDto = DeviceCach.getDeviceHost(deviceId); | ||
| 68 | + | ||
| 69 | + Topic topic = new Topic(); | ||
| 70 | + topic.setTopicType("HOST"); | ||
| 71 | + topic.setClientid(deviceId); | ||
| 72 | + topic.setRoleid(parserDeviceHostDto.getIotProduct().getRole_id()+""); | ||
| 73 | + topic.setUsername(parserDeviceHostDto.getIotProduct().getMqtt_username()); | ||
| 74 | + topic.setPayloadtype("Json"); | ||
| 75 | + topic.setMessageid(com.ruoyi.common.utils.DateUtils.getNowTimeMilly()+""); | ||
| 76 | + | ||
| 77 | + NoticeMessageDto noticeMessageDto = new NoticeMessageDto(); | ||
| 78 | + noticeMessageDto.setTopic(topic); | ||
| 79 | + noticeMessageDto.setTopicconfig(topicconfig); | ||
| 80 | + | ||
| 81 | + noticeMessageDto.setCommd(jsonObject.toString().getBytes()); | ||
| 82 | + | ||
| 83 | + return noticeMessageDto; | ||
| 84 | + } | ||
| 85 | + | ||
| 65 | 86 | ||
| 66 | private Topic getWriteTopic(String deviceId, IotProduct iotProduct) | 87 | private Topic getWriteTopic(String deviceId, IotProduct iotProduct) |
| 67 | { | 88 | { |
| @@ -49,6 +49,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | @@ -49,6 +49,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | ||
| 49 | return null; | 49 | return null; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | + @Override | ||
| 53 | + public NoticeMessageDto host(String deviceId, JsonObject jsonObject) { | ||
| 54 | + ParserDeviceHostDto parserDeviceHostDto = DeviceCach.getDeviceHost(deviceId); | ||
| 55 | + | ||
| 56 | + Topic topic = new Topic(); | ||
| 57 | + topic.setTopicType("HOST"); | ||
| 58 | + topic.setClientid(deviceId); | ||
| 59 | + topic.setRoleid(parserDeviceHostDto.getIotProduct().getRole_id()+""); | ||
| 60 | + topic.setUsername(parserDeviceHostDto.getIotProduct().getMqtt_username()); | ||
| 61 | + topic.setPayloadtype("Json"); | ||
| 62 | + topic.setMessageid(DateUtils.getNowTimeMilly()+""); | ||
| 63 | + | ||
| 64 | + NoticeMessageDto noticeMessageDto = new NoticeMessageDto(); | ||
| 65 | + noticeMessageDto.setTopic(topic); | ||
| 66 | + noticeMessageDto.setTopicconfig(topicModel); | ||
| 67 | + | ||
| 68 | + noticeMessageDto.setCommd(jsonObject.toString().getBytes()); | ||
| 69 | + | ||
| 70 | + return noticeMessageDto; | ||
| 71 | + } | ||
| 72 | + | ||
| 52 | private Topic getWriteTopic(String deviceId, IotProduct iotProduct) | 73 | private Topic getWriteTopic(String deviceId, IotProduct iotProduct) |
| 53 | { | 74 | { |
| 54 | Topic topic = new Topic(); | 75 | Topic topic = new Topic(); |
| @@ -55,6 +55,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | @@ -55,6 +55,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | ||
| 55 | return null; | 55 | return null; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | + @Override | ||
| 59 | + public NoticeMessageDto host(String deviceId, JsonObject jsonObject) { | ||
| 60 | + ParserDeviceHostDto parserDeviceHostDto = DeviceCach.getDeviceHost(deviceId); | ||
| 61 | + | ||
| 62 | + Topic topic = new Topic(); | ||
| 63 | + topic.setTopicType("HOST"); | ||
| 64 | + topic.setClientid(deviceId); | ||
| 65 | + topic.setRoleid(parserDeviceHostDto.getIotProduct().getRole_id()+""); | ||
| 66 | + topic.setUsername(parserDeviceHostDto.getIotProduct().getMqtt_username()); | ||
| 67 | + topic.setPayloadtype("Json"); | ||
| 68 | + topic.setMessageid(DateUtils.getNowTimeMilly()+""); | ||
| 69 | + | ||
| 70 | + NoticeMessageDto noticeMessageDto = new NoticeMessageDto(); | ||
| 71 | + noticeMessageDto.setTopic(topic); | ||
| 72 | + noticeMessageDto.setTopicconfig(topicModel); | ||
| 73 | + | ||
| 74 | + noticeMessageDto.setCommd(jsonObject.toString().getBytes()); | ||
| 75 | + | ||
| 76 | + return noticeMessageDto; | ||
| 77 | + } | ||
| 78 | + | ||
| 58 | private Topic getWriteTopic(String deviceId, IotProduct iotProduct) | 79 | private Topic getWriteTopic(String deviceId, IotProduct iotProduct) |
| 59 | { | 80 | { |
| 60 | Topic topic = new Topic(); | 81 | Topic topic = new Topic(); |
| @@ -22,5 +22,9 @@ | @@ -22,5 +22,9 @@ | ||
| 22 | <groupId>com.zhonglai.luhui</groupId> | 22 | <groupId>com.zhonglai.luhui</groupId> |
| 23 | <artifactId>lh-device-protocol-factory</artifactId> | 23 | <artifactId>lh-device-protocol-factory</artifactId> |
| 24 | </dependency> | 24 | </dependency> |
| 25 | + <dependency> | ||
| 26 | + <groupId>com.infiniteautomation</groupId> | ||
| 27 | + <artifactId>modbus4j</artifactId> | ||
| 28 | + </dependency> | ||
| 25 | </dependencies> | 29 | </dependencies> |
| 26 | </project> | 30 | </project> |
| 1 | +package com.zhonglai.luhui.device.protocol.modbus.modbus; | ||
| 2 | +import com.serotonin.modbus4j.ModbusFactory; | ||
| 3 | +import com.serotonin.modbus4j.ModbusMaster; | ||
| 4 | +import com.serotonin.modbus4j.exception.ModbusInitException; | ||
| 5 | +import com.serotonin.modbus4j.ip.IpParameters; | ||
| 6 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 7 | +import com.serotonin.modbus4j.msg.*; | ||
| 8 | + | ||
| 9 | +import java.io.File; | ||
| 10 | +import java.util.*; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 通用点位解析类 | ||
| 14 | + */ | ||
| 15 | +public class ModbusPointReader { | ||
| 16 | + | ||
| 17 | + private ModbusMaster master; | ||
| 18 | + private List<PointConfig> points; | ||
| 19 | + | ||
| 20 | + public ModbusPointReader(String ip, int port, String configPath) throws Exception { | ||
| 21 | + // 初始化 Modbus Master | ||
| 22 | + IpParameters params = new IpParameters(); | ||
| 23 | + params.setHost(ip); | ||
| 24 | + params.setPort(port); | ||
| 25 | + | ||
| 26 | + ModbusFactory factory = new ModbusFactory(); | ||
| 27 | + master = factory.createTcpMaster(params, false); | ||
| 28 | + master.init(); | ||
| 29 | + | ||
| 30 | + // 加载配置文件(JSON) | ||
| 31 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 32 | + points = Arrays.asList(mapper.readValue(new File(configPath), PointConfig[].class)); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public void readAll() throws Exception { | ||
| 36 | + for (PointConfig p : points) { | ||
| 37 | + Object value = readPoint(p); | ||
| 38 | + System.out.printf("设备: %s | 点位: %s | 值: %s%n", | ||
| 39 | + p.device, p.name, value); | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + private Object readPoint(PointConfig p) throws Exception { | ||
| 44 | + int slaveId = 1; // 您可以根据需要修改,这里使用固定的1 | ||
| 45 | + | ||
| 46 | + switch (p.type) { | ||
| 47 | + case "coil": | ||
| 48 | + ReadCoilsRequest coilsRequest = new ReadCoilsRequest(slaveId, p.address, p.length); | ||
| 49 | + ReadCoilsResponse coilsResponse = (ReadCoilsResponse) master.send(coilsRequest); | ||
| 50 | + return coilsResponse.getBooleanData()[0]; | ||
| 51 | + | ||
| 52 | + case "discreteInput": | ||
| 53 | + ReadDiscreteInputsRequest discreteRequest = new ReadDiscreteInputsRequest(slaveId, p.address, p.length); | ||
| 54 | + ReadDiscreteInputsResponse discreteResponse = (ReadDiscreteInputsResponse) master.send(discreteRequest); | ||
| 55 | + return discreteResponse.getBooleanData()[0]; | ||
| 56 | + | ||
| 57 | + case "holdingRegister": | ||
| 58 | + ReadHoldingRegistersRequest holdingRequest = new ReadHoldingRegistersRequest(slaveId, p.address, p.length); | ||
| 59 | + ReadHoldingRegistersResponse holdingResponse = (ReadHoldingRegistersResponse) master.send(holdingRequest); | ||
| 60 | + return parseRegister(holdingResponse.getShortData(), p); | ||
| 61 | + | ||
| 62 | + case "inputRegister": | ||
| 63 | + ReadInputRegistersRequest inputRequest = new ReadInputRegistersRequest(slaveId, p.address, p.length); | ||
| 64 | + ReadInputRegistersResponse inputResponse = (ReadInputRegistersResponse) master.send(inputRequest); | ||
| 65 | + return parseRegister(inputResponse.getShortData(), p); | ||
| 66 | + | ||
| 67 | + default: | ||
| 68 | + throw new IllegalArgumentException("未知点位类型: " + p.type); | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + private Object parseRegister(short[] regs, PointConfig p) { | ||
| 73 | + switch (p.datatype) { | ||
| 74 | + case "bool": | ||
| 75 | + return ((regs[0] >> p.bit) & 1) == 1; | ||
| 76 | + case "int16": | ||
| 77 | + return regs[0]; | ||
| 78 | + case "int32": | ||
| 79 | + return (regs[0] << 16) | (regs[1] & 0xFFFF); | ||
| 80 | + case "float": | ||
| 81 | + int raw = (regs[0] << 16) | (regs[1] & 0xFFFF); | ||
| 82 | + return Float.intBitsToFloat(raw); | ||
| 83 | + case "double": | ||
| 84 | + long raw64 = ((long) regs[0] << 48) | ((long) regs[1] << 32) | ((long) regs[2] << 16) | (regs[3] & 0xFFFF); | ||
| 85 | + return Double.longBitsToDouble(raw64); | ||
| 86 | + default: | ||
| 87 | + return regs[0]; | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + public void close() { | ||
| 92 | + if (master != null) master.destroy(); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + // 使用示例 | ||
| 96 | + public static void main(String[] args) throws Exception { | ||
| 97 | + ModbusPointReader reader = new ModbusPointReader("192.168.2.11", 2000, "points.json"); | ||
| 98 | + reader.readAll(); | ||
| 99 | + reader.close(); | ||
| 100 | + } | ||
| 101 | +} | ||
| 102 | + |
| 1 | +package com.zhonglai.luhui.device.protocol.modbus.modbus; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + *点位配置类 | ||
| 5 | + */ | ||
| 6 | +public class PointConfig { | ||
| 7 | + public String device; | ||
| 8 | + public String name; | ||
| 9 | + public String type; | ||
| 10 | + public int address; | ||
| 11 | + public int length; | ||
| 12 | + public String datatype; | ||
| 13 | + public int bit = 0; | ||
| 14 | +} |
| @@ -3,6 +3,7 @@ package com.zhonglai.luhui.device.protocol.plc004.control; | @@ -3,6 +3,7 @@ package com.zhonglai.luhui.device.protocol.plc004.control; | ||
| 3 | import com.google.gson.JsonElement; | 3 | import com.google.gson.JsonElement; |
| 4 | import com.google.gson.JsonObject; | 4 | import com.google.gson.JsonObject; |
| 5 | import com.google.gson.reflect.TypeToken; | 5 | import com.google.gson.reflect.TypeToken; |
| 6 | +import com.ruoyi.common.utils.DateUtils; | ||
| 6 | import com.ruoyi.common.utils.GsonConstructor; | 7 | import com.ruoyi.common.utils.GsonConstructor; |
| 7 | import com.zhonglai.luhui.device.analysis.comm.factory.Topic; | 8 | import com.zhonglai.luhui.device.analysis.comm.factory.Topic; |
| 8 | import com.zhonglai.luhui.device.domain.IotProduct; | 9 | import com.zhonglai.luhui.device.domain.IotProduct; |
| @@ -50,6 +51,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | @@ -50,6 +51,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | ||
| 50 | return null; | 51 | return null; |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 54 | + @Override | ||
| 55 | + public NoticeMessageDto host(String deviceId, JsonObject jsonObject) { | ||
| 56 | + ParserDeviceHostDto parserDeviceHostDto = DeviceCach.getDeviceHost(deviceId); | ||
| 57 | + | ||
| 58 | + Topic topic = new Topic(); | ||
| 59 | + topic.setTopicType("HOST"); | ||
| 60 | + topic.setClientid(deviceId); | ||
| 61 | + topic.setRoleid(parserDeviceHostDto.getIotProduct().getRole_id()+""); | ||
| 62 | + topic.setUsername(parserDeviceHostDto.getIotProduct().getMqtt_username()); | ||
| 63 | + topic.setPayloadtype("Json"); | ||
| 64 | + topic.setMessageid(DateUtils.getNowTimeMilly()+""); | ||
| 65 | + | ||
| 66 | + NoticeMessageDto noticeMessageDto = new NoticeMessageDto(); | ||
| 67 | + noticeMessageDto.setTopic(topic); | ||
| 68 | + noticeMessageDto.setTopicconfig(topicModel); | ||
| 69 | + | ||
| 70 | + noticeMessageDto.setCommd(jsonObject.toString().getBytes()); | ||
| 71 | + | ||
| 72 | + return noticeMessageDto; | ||
| 73 | + } | ||
| 74 | + | ||
| 53 | /** | 75 | /** |
| 54 | * 将嵌套的JsonObject扁平化。 | 76 | * 将嵌套的JsonObject扁平化。 |
| 55 | * | 77 | * |
| @@ -52,6 +52,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | @@ -52,6 +52,27 @@ public class DeviceCommandListenServiceImpl implements DeviceCommandServiceFacto | ||
| 52 | return null; | 52 | return null; |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | + @Override | ||
| 56 | + public NoticeMessageDto host(String deviceId, JsonObject jsonObject) { | ||
| 57 | + ParserDeviceHostDto parserDeviceHostDto = DeviceCach.getDeviceHost(deviceId); | ||
| 58 | + | ||
| 59 | + Topic topic = new Topic(); | ||
| 60 | + topic.setTopicType("HOST"); | ||
| 61 | + topic.setClientid(deviceId); | ||
| 62 | + topic.setRoleid(parserDeviceHostDto.getIotProduct().getRole_id()+""); | ||
| 63 | + topic.setUsername(parserDeviceHostDto.getIotProduct().getMqtt_username()); | ||
| 64 | + topic.setPayloadtype("Json"); | ||
| 65 | + topic.setMessageid(com.ruoyi.common.utils.DateUtils.getNowTimeMilly()+""); | ||
| 66 | + | ||
| 67 | + NoticeMessageDto noticeMessageDto = new NoticeMessageDto(); | ||
| 68 | + noticeMessageDto.setTopic(topic); | ||
| 69 | + noticeMessageDto.setTopicconfig(topicconfig); | ||
| 70 | + | ||
| 71 | + noticeMessageDto.setCommd(jsonObject.toString().getBytes()); | ||
| 72 | + | ||
| 73 | + return noticeMessageDto; | ||
| 74 | + } | ||
| 75 | + | ||
| 55 | 76 | ||
| 56 | private Topic getWriteTopic(String deviceId, IotProduct iotProduct) | 77 | private Topic getWriteTopic(String deviceId, IotProduct iotProduct) |
| 57 | { | 78 | { |
| @@ -146,6 +146,20 @@ public class DeviceCommandListenService implements RocketMQReplyListener<Message | @@ -146,6 +146,20 @@ public class DeviceCommandListenService implements RocketMQReplyListener<Message | ||
| 146 | }else { | 146 | }else { |
| 147 | return new Message(MessageCode.DEFAULT_FAIL_CODE,"指令发送失败"); | 147 | return new Message(MessageCode.DEFAULT_FAIL_CODE,"指令发送失败"); |
| 148 | } | 148 | } |
| 149 | + case host: | ||
| 150 | + | ||
| 151 | + noticeMessageDomain = deviceCommandServiceFactory.host(deviceCommand.getDeviceId(), jsonObject); | ||
| 152 | + | ||
| 153 | + if(null == noticeMessageDomain) | ||
| 154 | + { | ||
| 155 | + return new Message(MessageCode.DEFAULT_FAIL_CODE,"该设备不支持主机操作功能"); | ||
| 156 | + } | ||
| 157 | + if(clienNoticeServiceFactory.sendMessage(noticeMessageDomain)) | ||
| 158 | + { | ||
| 159 | + return new Message(MessageCode.DEFAULT_SUCCESS_CODE,"指令发送成功"); | ||
| 160 | + }else { | ||
| 161 | + return new Message(MessageCode.DEFAULT_FAIL_CODE,"指令发送失败"); | ||
| 162 | + } | ||
| 149 | default: | 163 | default: |
| 150 | return new Message(MessageCode.DEFAULT_FAIL_CODE,"指令类型不存在,请联系管理员"); | 164 | return new Message(MessageCode.DEFAULT_FAIL_CODE,"指令类型不存在,请联系管理员"); |
| 151 | } | 165 | } |
| @@ -11,4 +11,6 @@ public interface DeviceCommandServiceFactory { | @@ -11,4 +11,6 @@ public interface DeviceCommandServiceFactory { | ||
| 11 | NoticeMessageDto write(String deviceId, JsonObject jsonObject); | 11 | NoticeMessageDto write(String deviceId, JsonObject jsonObject); |
| 12 | 12 | ||
| 13 | NoticeMessageDto notice(String deviceId, JsonObject jsonObject); | 13 | NoticeMessageDto notice(String deviceId, JsonObject jsonObject); |
| 14 | + | ||
| 15 | + NoticeMessageDto host(String deviceId, JsonObject jsonObject); | ||
| 14 | } | 16 | } |
| @@ -11,9 +11,12 @@ import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelItemB | @@ -11,9 +11,12 @@ import com.zhonglai.luhui.device.analysis.comm.dto.thingsmodels.ThingsModelItemB | ||
| 11 | import com.zhonglai.luhui.device.analysis.comm.factory.Topic; | 11 | import com.zhonglai.luhui.device.analysis.comm.factory.Topic; |
| 12 | import com.zhonglai.luhui.device.analysis.comm.util.DateUtils; | 12 | import com.zhonglai.luhui.device.analysis.comm.util.DateUtils; |
| 13 | import com.zhonglai.luhui.device.domain.IotThingsModel; | 13 | import com.zhonglai.luhui.device.domain.IotThingsModel; |
| 14 | +import com.zhonglai.luhui.device.protocol.factory.control.DeviceCommandListenService; | ||
| 14 | import com.zhonglai.luhui.device.protocol.factory.dto.*; | 15 | import com.zhonglai.luhui.device.protocol.factory.dto.*; |
| 15 | import com.zhonglai.luhui.device.protocol.factory.service.IotThingsModelService; | 16 | import com.zhonglai.luhui.device.protocol.factory.service.IotThingsModelService; |
| 16 | import org.apache.commons.lang3.EnumUtils; | 17 | import org.apache.commons.lang3.EnumUtils; |
| 18 | +import org.slf4j.Logger; | ||
| 19 | +import org.slf4j.LoggerFactory; | ||
| 17 | 20 | ||
| 18 | import java.util.ArrayList; | 21 | import java.util.ArrayList; |
| 19 | import java.util.List; | 22 | import java.util.List; |
| @@ -129,7 +132,7 @@ public class DefaultProtocolPurificationFactoryImpl implements ProtocolPurificat | @@ -129,7 +132,7 @@ public class DefaultProtocolPurificationFactoryImpl implements ProtocolPurificat | ||
| 129 | if(null != jsonElement && !jsonElement.isJsonNull() ) | 132 | if(null != jsonElement && !jsonElement.isJsonNull() ) |
| 130 | { | 133 | { |
| 131 | ThingsModelItemBase thingsModelItemBase = getThingsModelItemBase(thingsModel,jsonElement); | 134 | ThingsModelItemBase thingsModelItemBase = getThingsModelItemBase(thingsModel,jsonElement); |
| 132 | - if(!thingsModelItemBase.checkValue()) | 135 | + if(null == thingsModelItemBase || !thingsModelItemBase.checkValue()) |
| 133 | { | 136 | { |
| 134 | continue; | 137 | continue; |
| 135 | } | 138 | } |
| @@ -170,16 +173,31 @@ public class DefaultProtocolPurificationFactoryImpl implements ProtocolPurificat | @@ -170,16 +173,31 @@ public class DefaultProtocolPurificationFactoryImpl implements ProtocolPurificat | ||
| 170 | return null; | 173 | return null; |
| 171 | } | 174 | } |
| 172 | 175 | ||
| 173 | - private ThingsModelItemBase getThingsModelItemBase(IotThingsModel thingsModel,JsonElement jsonElement) | ||
| 174 | - { | ||
| 175 | - String data_type = thingsModel.getData_type().toUpperCase(); | ||
| 176 | - if(!EnumUtils.isValidEnum(ThingsModelDataTypeEnum.class,data_type)) | ||
| 177 | - { | ||
| 178 | - data_type = ThingsModelDataTypeEnum.STRING.name(); | 176 | + private ThingsModelItemBase getThingsModelItemBase(IotThingsModel thingsModel, JsonElement jsonElement) { |
| 177 | + String dataType = thingsModel.getData_type(); | ||
| 178 | + ThingsModelDataTypeEnum thingsModelDataTypeEnum; | ||
| 179 | + | ||
| 180 | + try { | ||
| 181 | + if (dataType == null) { | ||
| 182 | + thingsModelDataTypeEnum = ThingsModelDataTypeEnum.STRING; | ||
| 183 | + } else { | ||
| 184 | + // 统一转大写再校验 | ||
| 185 | + String upperType = dataType.toUpperCase(); | ||
| 186 | + if (EnumUtils.isValidEnum(ThingsModelDataTypeEnum.class, upperType)) { | ||
| 187 | + thingsModelDataTypeEnum = Enum.valueOf(ThingsModelDataTypeEnum.class, upperType); | ||
| 188 | + } else { | ||
| 189 | + thingsModelDataTypeEnum = ThingsModelDataTypeEnum.STRING; | ||
| 190 | + } | ||
| 179 | } | 191 | } |
| 180 | - return ThingsModelItemBase.newhingsModel(Enum.valueOf(ThingsModelDataTypeEnum.class,data_type),thingsModel, jsonElement); | 192 | + } catch (IllegalArgumentException | NullPointerException e) { |
| 193 | + // 容错处理,回退到 STRING | ||
| 194 | + thingsModelDataTypeEnum = ThingsModelDataTypeEnum.STRING; | ||
| 181 | } | 195 | } |
| 182 | 196 | ||
| 197 | + return ThingsModelItemBase.newhingsModel(thingsModelDataTypeEnum, thingsModel, jsonElement); | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + | ||
| 183 | private DeviceSensorData getDeviceSensorData(Integer time,String sensorNumber,Topic topic,IotThingsModel thingsModel,ThingsModelItemBase thingsModelItemBase) | 201 | private DeviceSensorData getDeviceSensorData(Integer time,String sensorNumber,Topic topic,IotThingsModel thingsModel,ThingsModelItemBase thingsModelItemBase) |
| 184 | { | 202 | { |
| 185 | DeviceSensorData sensorData = new DeviceSensorData(); | 203 | DeviceSensorData sensorData = new DeviceSensorData(); |
| @@ -38,6 +38,7 @@ | @@ -38,6 +38,7 @@ | ||
| 38 | <module>lh-ssh-service-lesten</module> | 38 | <module>lh-ssh-service-lesten</module> |
| 39 | <module>lh-deviceInfo-sync</module> | 39 | <module>lh-deviceInfo-sync</module> |
| 40 | <module>lh-camera</module> | 40 | <module>lh-camera</module> |
| 41 | + <module>lh-device-modbus-terminal</module> | ||
| 41 | </modules> | 42 | </modules> |
| 42 | 43 | ||
| 43 | <properties> | 44 | <properties> |
| @@ -390,6 +390,12 @@ | @@ -390,6 +390,12 @@ | ||
| 390 | <artifactId>lh-jar-ssh-proxy</artifactId> | 390 | <artifactId>lh-jar-ssh-proxy</artifactId> |
| 391 | <version>${ruoyi.version}</version> | 391 | <version>${ruoyi.version}</version> |
| 392 | </dependency> | 392 | </dependency> |
| 393 | + <!-- mqtt终端插件 --> | ||
| 394 | + <dependency> | ||
| 395 | + <groupId>com.zhonglai.luhui</groupId> | ||
| 396 | + <artifactId>lh-device-mqtt-terminal-jar</artifactId> | ||
| 397 | + <version>${ruoyi.version}</version> | ||
| 398 | + </dependency> | ||
| 393 | <!-- 支持data --> | 399 | <!-- 支持data --> |
| 394 | <dependency> | 400 | <dependency> |
| 395 | <groupId>org.projectlombok</groupId> | 401 | <groupId>org.projectlombok</groupId> |
| @@ -627,6 +633,24 @@ | @@ -627,6 +633,24 @@ | ||
| 627 | <artifactId>tess4j</artifactId> | 633 | <artifactId>tess4j</artifactId> |
| 628 | <version>5.4.0</version> | 634 | <version>5.4.0</version> |
| 629 | </dependency> | 635 | </dependency> |
| 636 | + | ||
| 637 | + <!-- https://mvnrepository.com/artifact/com.infiniteautomation/modbus4j --> | ||
| 638 | + <dependency> | ||
| 639 | + <groupId>com.infiniteautomation</groupId> | ||
| 640 | + <artifactId>modbus4j</artifactId> | ||
| 641 | + <version>3.1.0</version> | ||
| 642 | + </dependency> | ||
| 643 | + <dependency> | ||
| 644 | + <groupId>com.fazecast</groupId> | ||
| 645 | + <artifactId>jSerialComm</artifactId> | ||
| 646 | + <version>2.11.2</version> | ||
| 647 | + </dependency> | ||
| 648 | + | ||
| 649 | + <dependency> | ||
| 650 | + <groupId>org.ini4j</groupId> | ||
| 651 | + <artifactId>ini4j</artifactId> | ||
| 652 | + <version>0.5.4</version> | ||
| 653 | + </dependency> | ||
| 630 | </dependencies> | 654 | </dependencies> |
| 631 | 655 | ||
| 632 | 656 |
| 1 | -# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2024 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: uploadPath # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8086 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # web: # resources: # static-locations: classpath:/static/, classpath:/templates/ # token配置 token: # 令牌自定义标识 header: Authorization # 令牌有效期(默认30分钟) expireTime: 31536000 # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /api sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: - /doc.html - /webjars/** - /swagger-ui.html - /v2/api-docs/** - /swagger/api-docs - /token/code - /swagger-resources/** - /login/** - /profile/** chatgpt: token: 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 # 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 timeout: 5000 apiHost: https://api.openai.com/ proxy: isProxy: true host: 127.0.0.1 port: 7890 | ||
| 1 | +# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2024 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: uploadPath # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8086 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # web: # resources: # static-locations: classpath:/static/, classpath:/templates/ # token配置 token: # 令牌自定义标识 header: Authorization # 令牌有效期(默认30分钟) expireTime: 31536000 # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /api sys: # 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: - /doc.html - /webjars/** - /swagger-ui.html - /v2/api-docs/** - /swagger/api-docs - /token/code - /swagger-resources/** - /login/** - /profile/** chatgpt: token: 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 # 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 timeout: 5000 apiHost: https://api.openai.com/ proxy: isProxy: true host: 127.0.0.1 port: 7890 |
-
请 注册 或 登录 后发表评论