xiuwei пре 1 година
родитељ
комит
a9774d36f9
73 измењених фајлова са 5419 додато и 5 уклоњено
  1. 43 2
      pom.xml
  2. 119 0
      src/main/java/container/DataPackerContainer.java
  3. 162 0
      src/main/java/container/ProtocolDataContainer.java
  4. 148 0
      src/main/java/container/ProtocolDataContainer_Local.java
  5. 190 0
      src/main/java/container/ProtocolTunnelContainer.java
  6. 188 0
      src/main/java/container/RecurringTaskContainer.java
  7. 1 1
      src/main/java/contorllogic/PidController.java
  8. 40 0
      src/main/java/datapacker/AbstractDataPacker.java
  9. 133 0
      src/main/java/datapacker/iml/EquipmentDataPacker.java
  10. 43 0
      src/main/java/ems/AbstractEMSController.java
  11. 31 0
      src/main/java/ems/ExistingEMS.java
  12. 36 0
      src/main/java/ems/InverterEMSController.java
  13. 79 0
      src/main/java/entity/BaseRealtimeData.java
  14. 44 0
      src/main/java/entity/BaseRtuTunnelInfo.java
  15. 45 0
      src/main/java/entity/BaseTcpMasterTunnelInfo.java
  16. 30 0
      src/main/java/entity/BaseTcpSlaverTunnelInfo.java
  17. 64 0
      src/main/java/entity/BaseTunnelInfo.java
  18. 76 0
      src/main/java/entity/CommandAttribute.java
  19. 70 0
      src/main/java/entity/EquipmentAttribute.java
  20. 82 0
      src/main/java/entity/PowerEquipment.java
  21. 9 0
      src/main/java/entity/datagetter/BaseDataGetter.java
  22. 16 0
      src/main/java/entity/datagetter/ContainerBoolDataGetter.java
  23. 37 0
      src/main/java/entity/datagetter/ContainerDataGetter.java
  24. 18 0
      src/main/java/entity/datagetter/ContainerNumDataGetter.java
  25. 20 0
      src/main/java/entity/datagetter/ManualSettingDataGetter.java
  26. 81 0
      src/main/java/entity/datapoint/BaseDataPoint.java
  27. 46 0
      src/main/java/entity/datapoint/CalculateDataPoint.java
  28. 133 0
      src/main/java/entity/datapoint/CalculatorOfDataPoint.java
  29. 92 0
      src/main/java/entity/datapoint/CommandDataPoint.java
  30. 31 0
      src/main/java/entity/datapoint/ConfigDataPoint.java
  31. 48 0
      src/main/java/entity/datapoint/GatherDataPoint.java
  32. 59 0
      src/main/java/entity/datapoint/SenderDataPoint.java
  33. 47 0
      src/main/java/enums/CommandProtocolType.java
  34. 116 0
      src/main/java/enums/DataType.java
  35. 69 0
      src/main/java/enums/EquipmentTypeEnum.java
  36. 136 0
      src/main/java/enums/TunnelStatus.java
  37. 86 0
      src/main/java/enums/TunnelType.java
  38. 23 0
      src/main/java/exception/DataExchangeException.java
  39. 81 0
      src/main/java/logger/TunnelLoggerFactory.java
  40. 35 0
      src/main/java/mastertunnelinfo/Master104TcpTunnelInfo.java
  41. 31 0
      src/main/java/mastertunnelinfo/MasterCdtRtuTunnelInfo.java
  42. 37 0
      src/main/java/mastertunnelinfo/MasterModbusRtuTunnelInfo.java
  43. 36 0
      src/main/java/mastertunnelinfo/MasterModbusTcpTunnelInfo.java
  44. 37 0
      src/main/java/slavertunnelinfo/Slaver104TcpTunnelInfo.java
  45. 30 0
      src/main/java/slavertunnelinfo/SlaverCdtRtuTunnelInfo.java
  46. 36 0
      src/main/java/slavertunnelinfo/SlaverModbusRtuTunnelInfo.java
  47. 36 0
      src/main/java/slavertunnelinfo/SlaverModbusTcpTunnelInfo.java
  48. 145 0
      src/main/java/tunnelworker/BaseProtocolTunnel.java
  49. 35 0
      src/main/java/tunnelworker/masters/MasterInterface.java
  50. 236 0
      src/main/java/tunnelworker/masters/iml/BaseModbusMaster.java
  51. 130 0
      src/main/java/tunnelworker/masters/iml/CdtRtuMaster.java
  52. 179 0
      src/main/java/tunnelworker/masters/iml/Iec104TcpMaster.java
  53. 96 0
      src/main/java/tunnelworker/masters/iml/ModbusRtuMaster.java
  54. 108 0
      src/main/java/tunnelworker/masters/iml/ModbusTcpMaster.java
  55. 11 0
      src/main/java/tunnelworker/package-info.java
  56. 23 0
      src/main/java/tunnelworker/slavers/SlaverInterface.java
  57. 119 0
      src/main/java/tunnelworker/slavers/iml/CdtRtuSlaver.java
  58. 161 0
      src/main/java/tunnelworker/slavers/iml/Iec104TcpSlaver.java
  59. 139 0
      src/main/java/tunnelworker/slavers/iml/ModbusRtuSlaver.java
  60. 129 0
      src/main/java/tunnelworker/slavers/iml/ModbusTcpSlaver.java
  61. 77 0
      src/main/java/tunnelworker/workassist/SingleThreadPoolExecutorUtil.java
  62. 69 0
      src/main/java/tunnelworker/workassist/TunnelBuilder.java
  63. 93 0
      src/main/java/tunnelworker/workassist/cdt/MyCDTDataHandler.java
  64. 161 0
      src/main/java/tunnelworker/workassist/cdt/MyCDTDataTransmitter.java
  65. 69 0
      src/main/java/tunnelworker/workassist/iec104tcp/HandelBoolean.java
  66. 69 0
      src/main/java/tunnelworker/workassist/iec104tcp/HandelNoQualityNormalizedInteger.java
  67. 78 0
      src/main/java/tunnelworker/workassist/iec104tcp/HandelNormalizedInteger.java
  68. 33 0
      src/main/java/tunnelworker/workassist/iec104tcp/HandleNormalizationValueCommand.java
  69. 66 0
      src/main/java/tunnelworker/workassist/iec104tcp/HandleShortFloat.java
  70. 35 0
      src/main/java/tunnelworker/workassist/iec104tcp/HandleShortFloatCommand.java
  71. 32 0
      src/main/java/tunnelworker/workassist/iec104tcp/HandleSingleBooleanCommand.java
  72. 71 0
      src/main/java/tunnelworker/workassist/iec104tcp/HandleTotalSummon.java
  73. 5 2
      src/test/java/Test.java

+ 43 - 2
pom.xml

@@ -16,10 +16,51 @@
         </dependency>
         <dependency>
             <groupId>wei.yigulu</groupId>
-            <artifactId>protocol-iec104</artifactId>
-            <version>2.4.23</version>
+            <artifactId>protocol-all</artifactId>
+            <version>2.3.16</version>
         </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus</artifactId>
+            <version>3.5.3.1</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>2.14.2</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.15.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.16</version>
+        </dependency>
+        <dependency>
+            <groupId>com.googlecode.aviator</groupId>
+            <artifactId>aviator</artifactId>
+            <version>5.0.0</version>
+        </dependency>
+
     </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 
 
 </project>

+ 119 - 0
src/main/java/container/DataPackerContainer.java

@@ -0,0 +1,119 @@
+package container;
+
+
+
+
+import datapacker.AbstractDataPacker;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 实体类装载器的容器
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+public class DataPackerContainer {
+
+
+	/**
+	 * 各种设备状态对应的service
+	 */
+	RecurringTaskContainer recurringTaskContainer = RecurringTaskContainer.getInstance();
+
+	/**
+	 * packer 的池子
+	 */
+	private Map<String, AbstractDataPacker> packers = new ConcurrentHashMap<>();
+
+	private Map<String, Callable> tasks = new ConcurrentHashMap<>();
+
+	private DataPackerContainer() {
+	}
+
+	/**
+	 * 返回容器对象
+	 *
+	 * @return the instance
+	 */
+	public static final DataPackerContainer getInstance() {
+		return LazyHolder.INSTANCE;
+	}
+
+	/**
+	 * 添加packer  并启动定时任务
+	 *
+	 * @param equipmentNo 设备NO
+	 * @param packer      组装器
+	 * @return {@link  AbstractDataPacker}
+	 */
+	public synchronized Map<String, AbstractDataPacker> addPacker(String equipmentNo, AbstractDataPacker packer) {
+		this.packers.put(equipmentNo, packer);
+		tasks.put(equipmentNo, startPackageTask(packer));
+		return this.packers;
+	}
+
+	/**
+	 * 删除组装器
+	 * 移除packer
+	 *
+	 * @param equipmentNo 设备号
+	 * @return {@link AbstractDataPacker}
+	 */
+	public synchronized Map<String, AbstractDataPacker> removePacker(String equipmentNo) {
+		this.packers.remove(equipmentNo);
+		recurringTaskContainer.removeTask(this.tasks.get(equipmentNo));
+		this.tasks.remove(equipmentNo);
+		return this.packers;
+	}
+
+	/**
+	 * 获取packer
+	 *
+	 * @param equipmentNo 设备No
+	 * @return {@link AbstractDataPacker}
+	 */
+	public synchronized AbstractDataPacker getPacker(String equipmentNo) {
+		return this.packers.get(equipmentNo);
+	}
+
+	/**
+	 * 根据新增的设备 增加定时入库的定时任务
+	 *
+	 * @param equipmentDataPacker 设备数据组装器
+	 * @return {@link Callable}
+	 */
+	public Callable startPackageTask(AbstractDataPacker equipmentDataPacker) {
+	/*	Class clazz = equipmentDataPacker.getRealtimeData().getClass();
+		switch (clazz.getSimpleName()) {
+			case "AgcRealtimeData":
+				return recurringTaskContainer.addRecurringTask(equipmentDataPacker.getInterval(), "存入AGC实时数据任务", () -> SpringUtils.getBean(AgcRealtimeDataServiceImpl.class).save((AgcRealtimeData) equipmentDataPacker.packageData()));
+			case "AvcRealtimeData":
+				return recurringTaskContainer.addRecurringTask(equipmentDataPacker.getInterval(), "存入AVC实时数据任务", () -> SpringUtils.getBean(AvcRealtimeDataServiceImpl.class).save((AvcRealtimeData) equipmentDataPacker.packageData()));
+			case "EmsRealtimeData":
+				return recurringTaskContainer.addRecurringTask(equipmentDataPacker.getInterval(), "存入能管平台实时数据任务", () -> SpringUtils.getBean(EmsRealtimeDataServiceImpl.class).save((EmsRealtimeData) equipmentDataPacker.packageData()));
+			case "PvInverterRealtimeData":
+				return recurringTaskContainer.addRecurringTask(equipmentDataPacker.getInterval(), "存入逆变器实时数据任务", () -> SpringUtils.getBean(IPvInverterRealtimeDataService.class).save((PvInverterRealtimeData) equipmentDataPacker.packageData()));
+			case "WindTurbineRealtimeData":
+				return recurringTaskContainer.addRecurringTask(equipmentDataPacker.getInterval(), "存入风机实时数据任务", () -> SpringUtils.getBean(IWindTurbineRealtimeDataService.class).save((WindTurbineRealtimeData) (equipmentDataPacker.packageData())));
+			case "SvgRealtimeData":
+				return recurringTaskContainer.addRecurringTask(equipmentDataPacker.getInterval(), "存入SVG实时数据任务", () -> SpringUtils.getBean(ISvgRealtimeDataService.class).save((SvgRealtimeData) equipmentDataPacker.packageData()));
+			case "CollectionLineRealtimeData":
+				return recurringTaskContainer.addRecurringTask(equipmentDataPacker.getInterval(), "存入集电线实时数据任务", () -> SpringUtils.getBean(ICollectionLineRealtimeDataService.class).save((CollectionLineRealtimeData) equipmentDataPacker.packageData()));
+			default:
+				return null;
+		}*/
+		return null;
+	}
+
+
+
+
+
+	private static class LazyHolder {
+		private static final DataPackerContainer INSTANCE = new DataPackerContainer();
+	}
+
+}

+ 162 - 0
src/main/java/container/ProtocolDataContainer.java

@@ -0,0 +1,162 @@
+package container;
+
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+/**
+ * 协议采集到数据的容器
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+
+@Slf4j
+public abstract class ProtocolDataContainer {
+
+
+    private static final ProtocolDataContainer INSTANCE;
+
+    static {
+            INSTANCE=ProtocolDataContainer_Local.getInstance();
+    }
+
+    protected ProtocolDataContainer(){
+
+    }
+
+
+
+    /**
+     * 获取单例实例
+     *
+     * @return the instance
+     */
+    public static ProtocolDataContainer getInstance() {
+        return INSTANCE;
+    }
+
+    /**
+     * 向池子内置数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public abstract void putNumber(String point, BigDecimal value);
+
+
+    /**
+     * 向池子内置采集数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public void putGNumber(Integer point, BigDecimal value) {
+        this.putNumber("G" + point, value);
+    }
+
+
+    /**
+     * 向池子内置配置数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public void putCNumber(Integer point, BigDecimal value) {
+        this.putNumber("C" + point, value);
+    }
+
+    /**
+     * 向池子内置计算得出数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public void putJNumber(Integer point, BigDecimal value) {
+        this.putNumber("J" + point, value);
+    }
+
+
+    /**
+     * 向池子内置数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public void putGBoolean(Integer point, Boolean value) {
+        this.putBoolean("G" + point, value);
+    }
+
+
+    /**
+     * 向池子内置数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public abstract void putBoolean(String point, Boolean value);
+
+    /**
+     * 向池子内置数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public void putCBoolean(Integer point, Boolean value) {
+        this.putBoolean("C" + point, value);
+    }
+
+    /**
+     * 向池子内置数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public void putJBoolean(Integer point, Boolean value) {
+        this.putBoolean("J" + point, value);
+    }
+
+
+    /**
+     * 获取到缓存池内的数值 如果遥测池内没有但是存在于遥信池 将返回 0or1
+     *
+     * @param point 点位
+     * @return Number number
+     */
+    public abstract BigDecimal getNumber(String point);
+
+    /**
+     * 获取到返回池内的遥信数据  如果遥信池内没有 遥测池内有 那么大于0的数值 将会返回ture 否则为false
+     *
+     * @param point 点位
+     * @return Boolean boolean
+     */
+    public abstract Boolean getBoolean(String point);
+
+    /**
+     * 遥信数据池是否含有该点位
+     *
+     * @param point point
+     * @return the boolean
+     */
+    public abstract Boolean isYxContains(String point);
+
+    /**
+     * 遥测数据池是否含有该点位值
+     *
+     * @param point point
+     * @return the boolean
+     */
+    public abstract Boolean isYcContains(String point);
+
+
+    public abstract Map<String, Boolean> getYxMap();
+
+    public abstract Map<String, BigDecimal> getYcMap();
+
+
+
+
+}

+ 148 - 0
src/main/java/container/ProtocolDataContainer_Local.java

@@ -0,0 +1,148 @@
+package container;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 协议采集到数据的容器
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+
+@Slf4j
+public class ProtocolDataContainer_Local extends ProtocolDataContainer {
+
+
+    /**
+     * 遥测数据
+     */
+    private Map<String, BigDecimal> yc = new ConcurrentHashMap<>();
+    /**
+     * 遥信数据
+     */
+    private Map<String, Boolean> yx = new ConcurrentHashMap<>();
+
+
+    private ProtocolDataContainer_Local() {
+    }
+
+    /**
+     * 获取单例实例
+     *
+     * @return the instance
+     */
+    public static final ProtocolDataContainer_Local getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    /**
+     * 向池子内置数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public void putNumber(String point, BigDecimal value) {
+        if (point == null || point == "") {
+            throw new IllegalArgumentException("存入数据池数据,入参点位为空");
+        }
+        if (value == null) {
+            throw new IllegalArgumentException("存入数据池数据,入值点位为空");
+        }
+        this.yc.put(point, value.setScale(2, RoundingMode.HALF_UP));
+    }
+
+
+
+    /**
+     * 向池子内置数据
+     *
+     * @param point point
+     * @param value value
+     */
+    public void putBoolean(String point, Boolean value) {
+        this.yx.put(point, value);
+    }
+
+
+
+
+    /**
+     * 获取到缓存池内的数值 如果遥测池内没有但是存在于遥信池 将返回 0or1
+     *
+     * @param point 点位
+     * @return Number number
+     */
+    public BigDecimal getNumber(String point) {
+        if (isYcContains(point)) {
+            return this.yc.get(point);
+        } else if (isYxContains(point)) {
+            return this.yx.get(point) ? BigDecimal.valueOf(1D) : BigDecimal.valueOf(0D);
+        } else {
+            return BigDecimal.valueOf(-99D);
+        }
+    }
+
+    /**
+     * 获取到返回池内的遥信数据  如果遥信池内没有 遥测池内有 那么大于0的数值 将会返回ture 否则为false
+     *
+     * @param point 点位
+     * @return Boolean boolean
+     */
+    public Boolean getBoolean(String point) {
+        if (isYxContains(point)) {
+            return this.yx.get(point);
+        } else if (isYcContains(point)) {
+            return this.yc.get(point).doubleValue() > 0;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 遥信数据池是否含有该点位
+     *
+     * @param point point
+     * @return the boolean
+     */
+    public Boolean isYxContains(String point) {
+        return this.yx.containsKey(point);
+    }
+
+    /**
+     * 遥测数据池是否含有该点位值
+     *
+     * @param point point
+     * @return the boolean
+     */
+    public Boolean isYcContains(String point) {
+        return this.yc.containsKey(point);
+    }
+
+
+    @Override
+    public String toString() {
+        JSONObject object = new JSONObject();
+        object.put("yx", this.yx);
+        object.put("yc", this.yc);
+        return object.toJSONString();
+    }
+
+    public Map<String, BigDecimal> getYcMap() {
+        return this.yc;
+    }
+
+    public Map<String, Boolean> getYxMap() {
+        return this.yx;
+    }
+
+
+    private static class LazyHolder {
+        private static final ProtocolDataContainer_Local INSTANCE = new ProtocolDataContainer_Local();
+    }
+
+}

+ 190 - 0
src/main/java/container/ProtocolTunnelContainer.java

@@ -0,0 +1,190 @@
+package container;
+
+import cn.hutool.core.collection.CollUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+
+import entity.BaseTunnelInfo;
+import enums.TunnelStatus;
+import exception.DataExchangeException;
+import lombok.extern.slf4j.Slf4j;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.masters.MasterInterface;
+import tunnelworker.slavers.SlaverInterface;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 装载通道对象的容器
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@Slf4j
+public class ProtocolTunnelContainer {
+
+    /**
+     * 通道集合
+     */
+    private Map<String, BaseProtocolTunnel> tunnels = new ConcurrentHashMap<>();
+
+
+    /**
+     * 定时任务集合
+     */
+    private Map<String, Callable> tasks = new ConcurrentHashMap<>();
+
+    /**
+     * 单例构造
+     */
+    private ProtocolTunnelContainer() {}
+
+    /**
+     * 单例
+     *
+     * @return 实例
+     */
+    public static final ProtocolTunnelContainer getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    /**
+     * 获取tunnelID下的 tunnel
+     *
+     * @param tunnelId 通道id
+     * @return BaseProtocolTunnel 通道
+     */
+    public BaseProtocolTunnel getTunnel(String tunnelId) {
+        if (this.tunnels.containsKey(tunnelId)) {
+            return this.tunnels.get(tunnelId);
+        }
+        return null;
+    }
+
+    /**
+     * 获取所有 tunnel 集合
+     *
+     * @param
+     * @return java.util.List<com.jiayue.agcavc.console.tunnelworker.BaseProtocolTunnel>
+     * @author L.ym
+     * @date 2022/12/6
+     **/
+    public List<BaseProtocolTunnel> getTunnelList() {
+        return CollUtil.newArrayList(this.tunnels.values());
+    }
+
+    /**
+     * 向集合中置入tunnel
+     *
+     * @param tunnel 通道对象
+     * @return 返回置入的tunnel
+     */
+    public BaseProtocolTunnel addTunnel(BaseProtocolTunnel tunnel) {
+        this.tunnels.put(tunnel.getTunnelId(), tunnel);
+        return tunnel;
+    }
+
+    /**
+     * 删除隧道 同时删除定时更新数据任务
+     *
+     * @param tunnelId 隧道id
+     */
+    public void removeTunnel(String tunnelId) {
+        this.tunnels.remove(tunnelId);
+        stopUpdateTask(tunnelId);
+    }
+
+    /**
+     * 停止更新任务
+     *
+     * @param tunnelId 通道id
+     */
+    public void stopUpdateTask(String tunnelId) {
+        if (this.tasks.containsKey(tunnelId)) {
+            RecurringTaskContainer.getInstance().removeTask(tasks.get(tunnelId));
+            this.tasks.remove(tunnelId);
+        }
+    }
+
+    /**
+     * 添加数据读取任务
+     *
+     * @param baseProtocolTunnel 通道对象
+     */
+    public void addUpdateDateTask(BaseProtocolTunnel baseProtocolTunnel) {
+        if (this.tasks.containsKey(baseProtocolTunnel.getTunnelId())) {
+            RecurringTaskContainer.getInstance().removeTask(this.tasks.get(baseProtocolTunnel.getTunnelId()));
+        }
+        Callable callable = null;
+        if (baseProtocolTunnel instanceof MasterInterface) {
+            callable = RecurringTaskContainer.getInstance().addRecurringTask(baseProtocolTunnel.getTunnelInfo().getRefreshInterval(), "采集通道数据更新任务" + baseProtocolTunnel.getTunnelInfo().getTunnelName(), () -> {
+                ((MasterInterface) baseProtocolTunnel).getData();
+                return null;
+            });
+        } else if (baseProtocolTunnel instanceof SlaverInterface) {
+            callable = RecurringTaskContainer.getInstance().addRecurringTask(baseProtocolTunnel.getTunnelInfo().getRefreshInterval(), "转发通道数据更新任务" + baseProtocolTunnel.getTunnelInfo().getTunnelName(), () -> {
+                ((SlaverInterface) baseProtocolTunnel).updateData2Protocol();
+                return null;
+            });
+        }
+        this.tasks.put(baseProtocolTunnel.getTunnelId(), callable);
+    }
+
+    @Override
+    public String toString() {
+        Map<String, JSONObject> str = new HashMap<>();
+        this.tunnels.forEach((k, v) ->
+                str.put(k, v.getJSONObj())
+        );
+        return JSON.toJSONString(str);
+    }
+
+    /**
+     * 静态内部类 为了创建安全的单例
+     */
+    private static class LazyHolder {
+        private static final ProtocolTunnelContainer INSTANCE = new ProtocolTunnelContainer();
+    }
+
+    /**
+     * 创建一个对通道池内通道状态的检查任务
+     */
+    class CheckTunnelStatusTask implements Callable {
+        @Override
+        public Object call() throws Exception {
+            TunnelStatus tunnelStatus;
+            log.debug("开始对所有的通道状态进行巡检");
+            Iterator<String> it = tunnels.keySet().iterator();
+            String keyString;
+            BaseProtocolTunnel tunnel;
+            BaseTunnelInfo tunnelInfo;
+            while (it.hasNext()) {
+                keyString = it.next();
+                tunnel = tunnels.get(keyString);
+                tunnelInfo = tunnel.getTunnelInfo();
+                tunnelStatus = tunnel.getTunnelStatus();
+                if (!TunnelStatus.ABLE_STATUS.contains(tunnelStatus)) {
+                    log.warn("发现本该开启的通道:{} 处在未开启成功的状态,重启该通道", tunnelInfo.getTunnelName());
+                    try {
+                        tunnel.tunnelStop();
+                    } catch (DataExchangeException e) {
+                        log.error("通道{}关闭时发生异常", tunnelInfo.getTunnelName(), e);
+                    }
+                    tunnel.refreshTunnelInfo(tunnelInfo).build();
+                    try {
+                        tunnel.startTunnel();
+                    } catch (DataExchangeException e) {
+                        log.error("通道{}开启时发生异常", tunnelInfo.getTunnelName(), e);
+                    }
+                }
+            }
+            return null;
+        }
+    }
+}

+ 188 - 0
src/main/java/container/RecurringTaskContainer.java

@@ -0,0 +1,188 @@
+package container;
+
+import cn.hutool.core.thread.ThreadFactoryBuilder;
+import lombok.extern.slf4j.Slf4j;
+import org.joda.time.DateTime;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.*;
+
+/**
+ * 循环反复任务池
+ * 会有线程查看线程任务执行时间 不允许有执行时间超过两秒的线程
+ * 最小执行间隔时间为3秒 因为 检查任务线程的频率为2s
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Slf4j
+public class RecurringTaskContainer {
+
+    /**
+     * 核心数量为10 排序数量为100 默认排序规则  多余线程存活时间为60秒的 定长线程
+     * 用于执行定时任务
+     */
+    private static final ExecutorService calculatorThread = new ThreadPoolExecutor(15, 1000,
+            60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactoryBuilder().setNamePrefix("MyRecurringTaskThread-").build());
+    /**
+     * 核心运行数量为4的定时任务池 用于拉起定时任务
+     */
+    private static final ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4, new ThreadFactoryBuilder().setNamePrefix("MyRecurringManagerThread-").build());
+    /**
+     * 定时任务对象  以时间间隔进行区分
+     */
+    private Map<Integer, Map<Callable, String>> tasks = new ConcurrentHashMap();
+
+    private RecurringTaskContainer() {
+    }
+
+    /**
+     * 单例
+     *
+     * @return 实例
+     */
+    public static final RecurringTaskContainer getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    /**
+     * 向定时任务集合里添加定时任务
+     *
+     * @param interval 时间间隔
+     * @param task     任务
+     * @return 返回添加的任务
+     */
+    public <T> Callable addRecurringCallable(Integer interval, String describe, Callable<T> task) {
+        if (interval < 2) {
+            throw new RuntimeException("该工具不允许执行3s以下间隔的任务");
+        }
+        log.info("循环任务池添加任务-{}", describe);
+        if (getTasks().containsKey(interval)) {
+            getTasks().get(interval).put(task, describe);
+        } else {
+            Map<Callable, String> map = new ConcurrentHashMap();
+            map.put(task, describe);
+            getTasks().put(interval, map);
+            startScheduledTask(interval);
+        }
+        return task;
+    }
+
+    /**
+     * 向定时任务集合里添加定时任务
+     *
+     * @param interval 时间间隔
+     * @param task     任务
+     * @return 返回添加的任务
+     */
+    public <T> Callable addRecurringTask(Integer interval, String describe, Task4Recurring<T> task) {
+        if (interval < 2) {
+            throw new RuntimeException("该工具不允许执行3s以下间隔的任务");
+        }
+        Callable<T> callable = () -> {
+            T o;
+            try {
+                o = task.task();
+            } catch (Exception e) {
+                log.error("定时任务本身产生异常", e);
+                throw e;
+            }
+            return o;
+        };
+        return addRecurringCallable(interval, describe, callable);
+    }
+
+    /**
+     * 移除定时任务
+     *
+     * @param interval 时间间隔
+     * @param task     执行的任务线程
+     */
+    public void removeTask(Integer interval, Callable task) {
+        if (this.getTasks().containsKey(interval)) {
+            this.getTasks().get(interval).remove(task);
+        }
+    }
+
+    private synchronized Map<Integer, Map<Callable, String>> getTasks() {
+        return this.tasks;
+    }
+
+    /**
+     * 移除定时任务
+     *
+     * @param task 任务线程
+     */
+    public void removeTask(Callable task) {
+        for (Integer key : getTasks().keySet()) {
+            if (this.getTasks().get(key).containsKey(task)) {
+                log.info("循环任务池移除任务-{}", this.getTasks().get(key).get(task));
+                this.getTasks().get(key).remove(task);
+                break;
+            }
+        }
+    }
+
+    /**
+     * 开启定时任务线程 用于拉起具体的定时任务
+     *
+     * @param interval 时间间隔
+     */
+    public void startScheduledTask(Integer interval) {
+        scheduledThreadPool.scheduleAtFixedRate(() -> {
+            final Map<Future, String> futures = new HashMap<>();
+            final DateTime startTime = DateTime.now();
+            Map<Callable, String> map = getTasks().get(interval);
+            Future future = null;
+            for (Callable c : map.keySet()) {
+                try {
+                    future = calculatorThread.submit(c);
+                    futures.put(future, map.get(c));
+                } catch (Exception e) {
+                    if (e instanceof RejectedExecutionException) {
+                        log.error("提交任务过多超过排序队列长度,任务被拒绝");
+                    } else {
+                        log.error("执行循环任务时发生异常", e);
+                    }
+                }
+            }
+            scheduledThreadPool.schedule(() -> {
+                log.trace("检查{}S循环任务执行情况,共提交{}个任务,任务提交时间{}", interval, futures.size(), startTime.toString("HH:mm:ss:SSS"));
+                Iterator<Future> iter = futures.keySet().iterator();
+                Future future1;
+                while (iter.hasNext()) {
+                    try {
+                        future1 = iter.next();
+                        if (!future1.isDone()) {
+                            log.info("发现未完成的超时任务{},任务提交时间为{},取消该任务", futures.get(future1), startTime.toString("HH:mm:ss:SSS"));
+                            future1.cancel(false);
+                        }
+                    } catch (Exception e) {
+                        log.error("检查任务完成情况时发生异常{}", interval, e);
+                    }
+                }
+            }, interval - 1, TimeUnit.SECONDS);
+        }, 0, interval, TimeUnit.SECONDS);
+
+    }
+
+    /**
+     * 它的意义就是为了打印一下日志 就是为了包裹一层try catch
+     *
+     * @param <T>
+     */
+    public interface Task4Recurring<T> {
+        T task() throws Exception;
+    }
+
+    /**
+     * 为了单例的内部类
+     *
+     * @return 实例
+     */
+    private static class LazyHolder {
+        private static final RecurringTaskContainer INSTANCE = new RecurringTaskContainer();
+    }
+}

+ 1 - 1
src/main/java/contorllogic/PidController.java

@@ -49,6 +49,6 @@ public class PidController {
         integral=integral.add(error);
         BigDecimal derivative = error.subtract(lastError);
         lastError = error;
-        return kp.multiply(error).add(ki.multiply(integral)).add(kd.multiply(derivative));
+        return kp.multiply(error).add(ki.multiply(integral)).add(kd.multiply(derivative)).add(input);
     }
 }

+ 40 - 0
src/main/java/datapacker/AbstractDataPacker.java

@@ -0,0 +1,40 @@
+package datapacker;
+
+
+import entity.BaseRealtimeData;
+import exception.DataExchangeException;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+/**
+ * 数据组装器
+ *
+ * @author: xiuwei
+ */
+
+public abstract class AbstractDataPacker {
+
+
+	@Getter
+	@Setter
+	protected BaseRealtimeData realtimeData;
+
+	/**
+	 * 入库时间间隔 单位S 默认1分钟
+	 */
+	@Setter
+	@Getter
+	@Accessors(chain = true)
+	protected Integer interval = 60;
+
+	/**
+	 * 包数据
+	 * 通过反射机制将容器中的数据封装入设备描述的实体类 同时对数据合理性进行校验 仅对数据类型为BigDecimal的数据进行合理性校验
+	 *
+	 * @return AbstractEquipmentData 设备状态数据
+	 * @throws DataExchangeException  异常
+	 * @throws IllegalAccessException 非法访问异常
+	 */
+	public abstract BaseRealtimeData packageData() throws DataExchangeException, IllegalAccessException;
+}

+ 133 - 0
src/main/java/datapacker/iml/EquipmentDataPacker.java

@@ -0,0 +1,133 @@
+package datapacker.iml;
+
+
+import container.ProtocolDataContainer;
+import datapacker.AbstractDataPacker;
+import entity.BaseRealtimeData;
+import entity.datapoint.BaseDataPoint;
+import exception.DataExchangeException;
+import lombok.Getter;
+import lombok.Setter;
+import org.joda.time.DateTime;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * 装备数据的包装类 点表转类属性
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@Getter
+public class EquipmentDataPacker extends AbstractDataPacker {
+
+    /**
+     * 设备号
+     */
+    @Setter
+    Integer equipmentNo;
+
+    /**
+     * 实时采集数据池
+     */
+    ProtocolDataContainer protocolDataContainerRedis = ProtocolDataContainer.getInstance();
+
+    /**
+     * 所有需要采集的数据点
+     */
+    private List<BaseDataPoint> pointList;
+
+    /**
+     * 将点位与类的属性对应上的map
+     */
+    private Map<BaseDataPoint, Field> point2FiledMap = new HashMap<>();
+
+    /**
+     * 构造方法,在方法中获取传入类型的属性,存入map中,以便后期使用
+     *
+     * @param newInstance T的实例 直接new 对象();
+     * @param pointList   点表集合
+     */
+    public EquipmentDataPacker(BaseRealtimeData newInstance, List<BaseDataPoint> pointList) {
+        setRealtimeData(newInstance);
+        if (pointList == null || pointList.size() == 0) {
+            return;
+        }
+        parseBaseDataPoint(pointList);
+    }
+
+    /**
+     * 解析采集数据点
+     *
+     * @param pointList 点列表
+     */
+    public void parseBaseDataPoint(List<BaseDataPoint> pointList) {
+        this.pointList = pointList;
+        this.equipmentNo = pointList.get(0).getEquipmentNo();
+        point2FiledMap = new HashMap<>();
+        List<Field> fields = new ArrayList<>();
+        fields.addAll(Arrays.asList(getRealtimeData().getClass().getDeclaredFields()));
+        fields.addAll(Arrays.asList(BaseRealtimeData.class.getDeclaredFields()));
+        for (Field f : fields) {
+            for (BaseDataPoint p : pointList) {
+                if (f.getName().equals(p.getEquipmentAttribute().getFieldName())) {
+                    f.setAccessible(true);
+                    point2FiledMap.put(p, f);
+                }
+            }
+        }
+    }
+
+    /**
+     * 通过反射机制将容器中的数据封装入设备描述的实体类 同时对数据合理性进行校验 仅对数据类型为BigDecimal的数据进行合理性校验
+     *
+     * @return AbstractEquipmentData 设备状态数据
+     * @throws DataExchangeException 异常
+     */
+    @Override
+    public synchronized BaseRealtimeData packageData() throws DataExchangeException {
+        BaseRealtimeData data;
+        try {
+            data = realtimeData.getClass().newInstance();
+        } catch (InstantiationException e) {
+            throw new DataExchangeException(40101, "传入实体类实例化失败");
+        } catch (IllegalAccessException e) {
+            throw new DataExchangeException(40102, "传入实体类构建实例时权限异常");
+        }
+        Type type;
+        if (this.point2FiledMap == null) {
+            return data;
+        }
+        for (Map.Entry<BaseDataPoint, Field> entry : point2FiledMap.entrySet()) {
+            String dpId = "";
+            try {
+                dpId = entry.getKey().getSId();
+                if (protocolDataContainerRedis.isYxContains(dpId)
+                    || protocolDataContainerRedis.isYcContains(dpId)) {
+                    type = entry.getValue().getGenericType();
+                    if (Boolean.class.equals(type) || boolean.class.equals(type)) {
+                        entry.getValue().set(data, protocolDataContainerRedis.getBoolean(dpId));
+                    } else if (BigDecimal.class.equals(type)) {
+                        entry.getValue().set(data,
+                            protocolDataContainerRedis.getNumber(dpId).setScale(2, BigDecimal.ROUND_HALF_UP));
+                    } else if (Integer.class.equals(type) || int.class.equals(type)) {
+                        entry.getValue().set(data, protocolDataContainerRedis.getNumber(dpId).intValue());
+                    }
+                }
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+                throw new DataExchangeException(40103, "传入实体域注入值时权限异常");
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+                throw new DataExchangeException(40104, "传入实体域注入值时类型异常");
+            }
+        }
+        data.setEquipmentNo(equipmentNo);
+        data.setTime(DateTime.now().withMillisOfSecond(0).withSecondOfMinute(0).toDate());
+        return data;
+    }
+
+}

+ 43 - 0
src/main/java/ems/AbstractEMSController.java

@@ -0,0 +1,43 @@
+package ems;
+
+import java.math.BigDecimal;
+
+/**
+ * EMS控制器
+ *
+ * @author 修唯
+ * @date 2023/07/13
+ */
+public abstract class AbstractEMSController {
+
+
+    /**
+     * 获取总有功功率
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal getTotalActivePower();
+
+    /**
+     * 获取总无功功率
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal getTotalReactivePower();
+
+    /**
+     * 设置总有功功率
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal setTotalActivePower(BigDecimal ap );
+
+    /**
+     * 设置总无功功率
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal setTotalReactivePower();
+
+
+}

+ 31 - 0
src/main/java/ems/ExistingEMS.java

@@ -0,0 +1,31 @@
+package ems;
+
+import java.math.BigDecimal;
+
+/**
+ * 现有ems
+ *
+ * @author 修唯
+ * @date 2023/07/17
+ */
+public class ExistingEMS  extends AbstractEMSController{
+    @Override
+    public BigDecimal getTotalActivePower() {
+        return null;
+    }
+
+    @Override
+    public BigDecimal getTotalReactivePower() {
+        return null;
+    }
+
+    @Override
+    public BigDecimal setTotalActivePower(BigDecimal ap) {
+        return null;
+    }
+
+    @Override
+    public BigDecimal setTotalReactivePower() {
+        return null;
+    }
+}

+ 36 - 0
src/main/java/ems/InverterEMSController.java

@@ -0,0 +1,36 @@
+package ems;
+
+import java.math.BigDecimal;
+
+/**
+ * 逆变器EMS 控制器
+ *
+ * @author 修唯
+ * @date 2023/07/17
+ */
+public class InverterEMSController extends AbstractEMSController{
+
+
+
+
+
+    @Override
+    public BigDecimal getTotalActivePower() {
+        return null;
+    }
+
+    @Override
+    public BigDecimal getTotalReactivePower() {
+        return null;
+    }
+
+    @Override
+    public BigDecimal setTotalActivePower(BigDecimal ap) {
+        return null;
+    }
+
+    @Override
+    public BigDecimal setTotalReactivePower() {
+        return null;
+    }
+}

+ 79 - 0
src/main/java/entity/BaseRealtimeData.java

@@ -0,0 +1,79 @@
+package entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 状态数据表
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Data
+public class BaseRealtimeData implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键
+     */
+    @TableId(type = IdType.AUTO)
+    Integer id;
+
+    /**
+     * 设备ID
+     */
+    Integer equipmentNo;
+
+    /**
+     * 时间
+     */
+    Date time;
+
+    /**
+     * 备用字段1
+     */
+    BigDecimal data1;
+    /**
+     * 备用字段2
+     */
+    BigDecimal data2;
+    /**
+     * 备用字段3
+     */
+    BigDecimal data3;
+    /**
+     * 备用字段4
+     */
+    BigDecimal data4;
+    /**
+     * 备用字段5
+     */
+    BigDecimal data5;
+    /**
+     * 备用字段6
+     */
+    BigDecimal data6;
+    /**
+     * 备用字段7
+     */
+    BigDecimal data7;
+    /**
+     * 备用字段8
+     */
+    BigDecimal data8;
+    /**
+     * 备用字段9
+     */
+    BigDecimal data9;
+    /**
+     * 备用字段10
+     */
+    BigDecimal data10;
+
+}

+ 44 - 0
src/main/java/entity/BaseRtuTunnelInfo.java

@@ -0,0 +1,44 @@
+package entity;
+
+import lombok.Data;
+
+/**
+ * Rtu通道的父类
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Data
+public class BaseRtuTunnelInfo<T> extends BaseTunnelInfo<T> {
+
+	/**
+	 * 串口名称
+	 */
+	private String serialName;
+	/**
+	 * 波特率
+	 */
+	private Integer baudRate;
+
+
+	/**
+	 * 数据位 5,6,7,8
+	 */
+	private Integer dataBits;
+
+	/**
+	 * 停止位  1 2  3(1.5)
+	 */
+	private Integer stopBits;
+
+	/**
+	 * 奇偶校验
+	 * PARITY_NONE = 0  无校验;
+	 * PARITY_ODD = 1  奇校验;
+	 * PARITY_EVEN = 2  偶校验;
+	 * PARITY_MARK = 3  0校验;
+	 * PARITY_SPACE = 4  1校验;
+	 */
+	private Integer parity;
+
+}

+ 45 - 0
src/main/java/entity/BaseTcpMasterTunnelInfo.java

@@ -0,0 +1,45 @@
+package entity;
+
+
+import entity.datapoint.GatherDataPoint;
+import lombok.Data;
+
+/**
+ * TCP Master的抽象实体
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Data
+public class BaseTcpMasterTunnelInfo extends BaseTunnelInfo<GatherDataPoint> {
+	/**
+	 * 远端ip
+	 */
+	private String remoteIp;
+
+	/**
+	 * 远端端口
+	 */
+	private Integer remotePort;
+
+	/**
+	 * 远端备用ip
+	 */
+	private String remoteSpareIp;
+
+	/**
+	 * 远端备用端口
+	 */
+	private Integer remoteSparePort;
+
+	/**
+	 * 自身的ip
+	 */
+	private String selfIp;
+
+	/**
+	 * 自身的端口
+	 */
+	private Integer selfPort;
+
+}

+ 30 - 0
src/main/java/entity/BaseTcpSlaverTunnelInfo.java

@@ -0,0 +1,30 @@
+package entity;
+
+
+import entity.datapoint.SenderDataPoint;
+import lombok.Data;
+
+/**
+ * Rtu通道的父类
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Data
+public class BaseTcpSlaverTunnelInfo extends BaseTunnelInfo<SenderDataPoint> {
+
+	/**
+	 * 自身端口
+	 */
+	private Integer selfPort;
+	/**
+	 * 自身ip
+	 */
+	private String selfIp;
+
+	/**
+	 * 远端ip
+	 */
+	private String remoteIp;
+
+}

+ 64 - 0
src/main/java/entity/BaseTunnelInfo.java

@@ -0,0 +1,64 @@
+package entity;
+
+
+import enums.EquipmentTypeEnum;
+import enums.TunnelType;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 通道信息的基类,用于与数据库交互传递信息的实体
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@Data
+public abstract class BaseTunnelInfo<T> {
+
+    /**
+     * 通道的id  使用String的UUID 或雪花值 方便后期的通道联合查询
+     */
+    //@TableId(type = IdType.INPUT)
+    protected String id;
+    /**
+     * 通道的名称
+     */
+    protected String tunnelName;
+
+    /**
+     * 通道的类型,冗余字段
+     */
+    //@EnumValue
+    protected TunnelType tunnelType;
+
+    /**
+     * 数据刷新时间间隔 单位s 秒
+     */
+    protected Integer refreshInterval;
+
+    /**
+     * 通道是否启用
+     */
+    protected Boolean isEnable;
+
+    /**
+     * 绑定的设备类型
+     */
+    protected EquipmentTypeEnum equipmentType;
+
+
+    /**
+     * 绑定的设备类型
+     */
+    protected Integer equipmentNo;
+
+
+    /**
+     * 通道中的点位
+     */
+   // @TableField(exist = false)
+    List<T> dataPoints;
+
+
+}

+ 76 - 0
src/main/java/entity/CommandAttribute.java

@@ -0,0 +1,76 @@
+package entity;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import enums.EquipmentTypeEnum;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 控制命令的属性
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Setter
+@Getter
+public class CommandAttribute {
+
+
+    /**
+     * ID
+     */
+    Integer id;
+
+    /**
+     * 命令方向
+     */
+    @EnumValue
+    CommandDirection commandDirection;
+    /**
+     * 设备类型
+     */
+    @EnumValue
+    EquipmentTypeEnum equipmentType;
+
+
+    /**
+     * 命令属性
+     */
+    String attribute;
+
+    /**
+     * 命令数据类型
+     */
+    @EnumValue
+    CommandDataType commandDataType;
+
+    /**
+     * 命令数据类型
+     *
+     * @author xiuwei
+     * @date 2021/06/15
+     */
+
+    public enum CommandDataType {
+        YX, YC
+    }
+
+    /**
+     * 命令方向
+     *
+     * @author xiuwei
+     * @date 2021/06/15
+     */
+    public enum CommandDirection {
+        /**
+         * 向外
+         */
+        OUTWARD,
+        /**
+         * 向内
+         */
+        INWARD
+    }
+
+
+}

+ 70 - 0
src/main/java/entity/EquipmentAttribute.java

@@ -0,0 +1,70 @@
+package entity;
+
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import enums.EquipmentTypeEnum;
+import lombok.Data;
+
+/**
+ * 设备属性
+ * 各个设备的属性值提取出来
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ * @date 2021/05/17
+ */
+@TableName(value = "EQUIPMENT_ATTRIBUTE")
+@Data
+public class EquipmentAttribute {
+
+	/**
+	 * Id
+	 */
+	@TableId(type = IdType.AUTO)
+	Integer id;
+
+	/**
+	 * 属性的定义
+	 */
+	String explanation;
+
+
+	/**
+	 * 属性所对应的的类的属性
+	 */
+	String fieldName;
+
+
+	/**
+	 * 计量单位
+	 */
+	String measurementUnits;
+
+
+	/**
+	 * Equipment type
+	 */
+	@JsonFormat(shape = JsonFormat.Shape.STRING)
+	EquipmentTypeEnum equipmentType;
+
+	/**
+	 * 字段类型
+	 */
+	@EnumValue
+	private EquipmentAttributeTypeEnum equipmentAttributeTypeEnum;
+
+	enum EquipmentAttributeTypeEnum {
+		/**
+		 * INVARIANT 固定字段 不可修改的
+		 * SPARE 备用字段
+		 * CUSTOM 自定义字段
+		 */
+		INVARIANT, SPARE, CUSTOM
+	}
+
+
+}

+ 82 - 0
src/main/java/entity/PowerEquipment.java

@@ -0,0 +1,82 @@
+package entity;
+
+import java.math.BigDecimal;
+
+/**
+ * 发电设备
+ *
+ * @author 修唯
+ * @date 2023/07/17
+ */
+public abstract class PowerEquipment {
+
+
+    /**
+     * 获取有功功率
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal getActivePower();
+
+    /**
+     * 获取无功功率
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal getReactivePower();
+
+    /**
+     * 设置有功功率
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal setActivePower(BigDecimal ap);
+
+    /**
+     * 设置无功功率
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal setReactivePower();
+
+
+    /**
+     * 获取有功调节上限
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal getActiveMaxPower();
+
+
+    /**
+     * 获取有功调节下限
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal getActiveMinPower();
+
+
+    /**
+     * 获取无功调节上限
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal getReactiveMaxPower();
+
+
+    /**
+     * 获取无功调节下限
+     *
+     * @return {@link BigDecimal}
+     */
+    public abstract BigDecimal getReactiveMinPower();
+
+
+
+
+
+
+
+
+
+}

+ 9 - 0
src/main/java/entity/datagetter/BaseDataGetter.java

@@ -0,0 +1,9 @@
+package entity.datagetter;
+
+/**
+ * 转发点表获取数值的获取类
+ */
+public abstract class BaseDataGetter<T> {
+    public abstract T getValue();
+
+}

+ 16 - 0
src/main/java/entity/datagetter/ContainerBoolDataGetter.java

@@ -0,0 +1,16 @@
+package entity.datagetter;
+
+
+import entity.datapoint.SenderDataPoint;
+
+public class ContainerBoolDataGetter extends ContainerDataGetter<Boolean> {
+
+    public ContainerBoolDataGetter(SenderDataPoint senderDataPoint) {
+        super(senderDataPoint);
+    }
+
+    @Override
+    public Boolean getValue() {
+        return protocolDataContainer.getBoolean(this.dataPointId);
+    }
+}

+ 37 - 0
src/main/java/entity/datagetter/ContainerDataGetter.java

@@ -0,0 +1,37 @@
+package entity.datagetter;
+
+
+
+import container.ProtocolDataContainer;
+import entity.datapoint.SenderDataPoint;
+
+import java.math.BigDecimal;
+
+/**
+ * 获取数据池数据的转发获取类
+ */
+public abstract class ContainerDataGetter<T> extends BaseDataGetter<T> {
+
+    ProtocolDataContainer protocolDataContainer = ProtocolDataContainer.getInstance();
+    /**
+     * 采集点表的ID 即数据池的ID
+     */
+    protected String dataPointId;
+
+    /**
+     * 倍率
+     */
+    protected BigDecimal ratio;
+
+
+    public ContainerDataGetter(SenderDataPoint senderDataPoint) {
+        this.dataPointId = senderDataPoint.getDataPointId();
+        this.ratio = senderDataPoint.getRatio();
+    }
+
+
+
+
+    @Override
+    public abstract T getValue() ;
+}

+ 18 - 0
src/main/java/entity/datagetter/ContainerNumDataGetter.java

@@ -0,0 +1,18 @@
+package entity.datagetter;
+
+
+import entity.datapoint.SenderDataPoint;
+
+import java.math.RoundingMode;
+
+public class ContainerNumDataGetter extends ContainerDataGetter<Number> {
+
+    public ContainerNumDataGetter(SenderDataPoint senderDataPoint) {
+        super(senderDataPoint);
+    }
+
+    @Override
+    public Number getValue() {
+        return protocolDataContainer.getNumber(this.dataPointId).multiply(this.ratio).setScale(2, RoundingMode.HALF_UP);
+    }
+}

+ 20 - 0
src/main/java/entity/datagetter/ManualSettingDataGetter.java

@@ -0,0 +1,20 @@
+package entity.datagetter;
+
+/**
+ * 手动设定值
+ */
+public class ManualSettingDataGetter<T> extends BaseDataGetter<T> {
+
+
+   T t;
+
+    public ManualSettingDataGetter(T t) {
+       this.t=t;
+    }
+
+
+    @Override
+    public T getValue() {
+        return null;
+    }
+}

+ 81 - 0
src/main/java/entity/datapoint/BaseDataPoint.java

@@ -0,0 +1,81 @@
+package entity.datapoint;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import entity.EquipmentAttribute;
+import enums.DataType;
+import lombok.Getter;
+import lombok.Setter;
+
+
+/**
+ * 点表的抽象类
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Setter
+@Getter
+public class BaseDataPoint {
+
+    @TableField(exist = false)
+    protected DataPointType dataPointType;
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+    /**
+     * 设备编号
+     */
+    private Integer equipmentNo;
+
+    /**
+     * 协议数据类型
+     */
+    @EnumValue
+    @JsonFormat(shape = JsonFormat.Shape.STRING)
+    private DataType dataType;
+
+    /**
+     * 设备属性
+     */
+     @TableField(exist = false)
+    private EquipmentAttribute equipmentAttribute;
+    /**
+     * 设备属性的ID冗余字段
+     */
+    private Integer equipmentAttributeId;
+
+    public void setEquipmentAttribute(EquipmentAttribute equipmentAttribute) {
+        this.equipmentAttribute = equipmentAttribute;
+        if (this.equipmentAttributeId == null || !this.equipmentAttributeId.equals(equipmentAttribute.getId())) {
+            this.equipmentAttributeId = equipmentAttribute.getId();
+        }
+    }
+
+    public void setEquipmentAttributeId(Integer equipmentAttributeId) {
+        this.equipmentAttributeId = equipmentAttributeId;
+        if (this.equipmentAttribute == null || !equipmentAttributeId.equals(this.equipmentAttribute.getId())) {
+            this.equipmentAttribute = new EquipmentAttribute();
+            this.equipmentAttribute.setId(this.equipmentAttributeId);
+        }
+    }
+
+    public String getSId() {
+        if (this instanceof ConfigDataPoint) {
+            return "C" + getId();
+        } else if (this instanceof GatherDataPoint) {
+            return "G" + getId();
+        } else if (this instanceof CalculateDataPoint) {
+            return "J" + getId();
+        }
+        return getId() + "";
+    }
+
+    public enum DataPointType {
+        CALCULATE, CONFIG, GATHER
+    }
+
+}

+ 46 - 0
src/main/java/entity/datapoint/CalculateDataPoint.java

@@ -0,0 +1,46 @@
+package entity.datapoint;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 计算的采集点表
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Setter
+@Getter
+@TableName(value = "CALCULATE_DATA_POINT", resultMap = "CalculateDataPointWithEA")
+public class CalculateDataPoint extends BaseDataPoint {
+
+    @EnumValue
+    private CalculateModel calculateModel;
+    /**
+     * 公式
+     */
+    private String formula;
+    /**
+     * 计算类名
+     */
+    private String calculateClassName;
+
+
+    public CalculateDataPoint() {
+        this.dataPointType = DataPointType.CALCULATE;
+    }
+
+
+    public enum CalculateModel {
+        /**
+         * 公式
+         */
+        FORMULA,
+        /**
+         * 类
+         */
+        CLASS
+    }
+}

+ 133 - 0
src/main/java/entity/datapoint/CalculatorOfDataPoint.java

@@ -0,0 +1,133 @@
+package entity.datapoint;
+
+import com.alibaba.fastjson.JSON;
+import com.googlecode.aviator.AviatorEvaluator;
+import com.googlecode.aviator.Expression;
+import com.googlecode.aviator.Options;
+import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
+
+import container.ProtocolDataContainer;
+import enums.DataType;
+import exception.DataExchangeException;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 计算点位的计算器
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Slf4j
+public class CalculatorOfDataPoint {
+
+
+	private static final Pattern expr = Pattern.compile("(?<=P_).*?(?=_P)");
+
+	static {
+		AviatorEvaluator.setOption(Options.ALWAYS_PARSE_FLOATING_POINT_NUMBER_INTO_DECIMAL, true);
+	}
+
+	/**
+	 * 计算表达式
+	 */
+	Expression compiledExp;
+	/**
+	 * 数据类型
+	 */
+	DataType dataType;
+	Integer id;
+	/**
+	 * 计算所需参数
+	 */
+	private Map<String, String> calculatingParameters;
+	/**
+	 * 计算所需公式
+	 */
+	@Getter
+	private String formula;
+	private ProtocolDataContainer protocolDataContainerRedis = ProtocolDataContainer.getInstance();
+
+	public CalculatorOfDataPoint(CalculateDataPoint calculateDataPoint) {
+		this.id = calculateDataPoint.getId();
+		this.dataType = calculateDataPoint.getDataType();
+		this.formula = calculateDataPoint.getFormula();
+	}
+
+	/**
+	 * 计算
+	 * 对公式进行计算
+	 *
+	 * @return V 计算完成的数据
+	 * @throws DataExchangeException 数据交换异常
+	 */
+	public void calculate() throws DataExchangeException {
+		Map<String, Object> values = new HashMap<>();
+		for (Map.Entry<String, String> e : getCalculatingParameters().entrySet()) {
+			if (protocolDataContainerRedis.isYcContains(e.getValue())) {
+				values.put(e.getKey(), protocolDataContainerRedis.getNumber(e.getValue()));
+			} else if (protocolDataContainerRedis.isYxContains(e.getValue())) {
+				values.put(e.getKey(), protocolDataContainerRedis.getNumber(e.getValue()));
+			} else {
+				log.error("数据池内无匹配点位:" + e.getValue());
+				return;
+			}
+		}
+		try {
+			if (this.dataType == DataType.YX) {
+				protocolDataContainerRedis.putBoolean("J" + this.id, (Boolean) (getOrInitCompiledExp().execute(values)));
+			} else {
+				protocolDataContainerRedis.putNumber("J" + this.id, (BigDecimal) (getOrInitCompiledExp().execute(values)));
+			}
+		} catch (ArithmeticException e) {
+			log.error("除数为0,计算公式为:{},请检查数据池内是否有该点位是否有数据", this.formula);
+		} catch (Exception e) {
+			log.error("数据计算{}时发生异常", this.formula, e);
+			log.info(JSON.toJSONString(values));
+			throw new DataExchangeException(10021, "计算器计算数据时发生异常,公式为:" + this.formula);
+		}
+	}
+
+	/**
+	 * 对公式进行初始化  如果公式解析失败将会抛出异常
+	 *
+	 * @return Expression  解析完成的表达式
+	 * @throws DataExchangeException 表达式解析失败时的异常
+	 */
+	private Expression getOrInitCompiledExp() throws DataExchangeException {
+		if (this.compiledExp == null) {
+			try {
+				this.compiledExp = AviatorEvaluator.compile(formula);
+			} catch (ExpressionSyntaxErrorException e) {
+				log.error("公式解析失败,公式不可用", e);
+				throw new DataExchangeException(50001, "公式解析失败,公式不可用");
+			}
+		}
+		return this.compiledExp;
+	}
+
+
+	/**
+	 * 对计算所需的参数进行整理
+	 *
+	 * @return P_(点位)_P  替换成真实的数据   字段--数值
+	 */
+	public Map<String, String> getCalculatingParameters() {
+		if (this.calculatingParameters == null) {
+			this.calculatingParameters = new HashMap<>();
+			Matcher matcher = expr.matcher(this.formula);
+			while (matcher.find()) {
+				String s = matcher.group();
+				this.calculatingParameters.put("P_" + s + "_P", s);
+			}
+		}
+		return this.calculatingParameters;
+	}
+
+}

+ 92 - 0
src/main/java/entity/datapoint/CommandDataPoint.java

@@ -0,0 +1,92 @@
+package entity.datapoint;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import entity.CommandAttribute;
+import enums.CommandProtocolType;
+import enums.DataType;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 命令控制点位
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Setter
+@Getter
+@TableName(value = "COMMAND_DATA_POINT", resultMap = "CommandDataPointWithCA")
+public class CommandDataPoint {
+
+
+	/**
+	 * id
+	 */
+	@TableId(type = IdType.AUTO)
+	Integer id;
+
+
+	/**
+	 * 通道id
+	 */
+	String tunnelId;
+
+	/**
+	 * 通道点位
+	 */
+	private Integer protocolPoint;
+
+	/**
+	 * 设备编号
+	 */
+	private Integer equipmentNo;
+
+	/**
+	 * 倍率
+	 */
+	private BigDecimal ratio;
+
+
+	/**
+	 * 命令属性id
+	 */
+	private Integer commandAttributeId;
+
+	/**
+	 * 数据类型
+	 */
+	@EnumValue
+	@JsonFormat(shape = JsonFormat.Shape.STRING)
+	private DataType dataType;
+
+	/**
+	 * 命令属性
+	 */
+	@TableField(exist = false)
+	private CommandAttribute commandAttribute;
+
+	@EnumValue
+	@JsonFormat(shape = JsonFormat.Shape.STRING)
+	private CommandProtocolType commandProtocolType;
+
+	public void setCommandAttribute(CommandAttribute commandAttribute) {
+		this.commandAttribute = commandAttribute;
+		if (this.commandAttributeId == null || !this.commandAttributeId.equals(commandAttribute.getId())) {
+			this.commandAttributeId = commandAttribute.getId();
+		}
+	}
+
+	public void setCommandAttributeId(Integer commandAttributeId) {
+		this.commandAttributeId = commandAttributeId;
+		if (this.commandAttribute == null || !commandAttributeId.equals(this.commandAttribute.getId())) {
+			this.commandAttribute = new CommandAttribute();
+			this.commandAttribute.setId(this.commandAttributeId);
+		}
+	}
+
+
+}

+ 31 - 0
src/main/java/entity/datapoint/ConfigDataPoint.java

@@ -0,0 +1,31 @@
+package entity.datapoint;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 配置项的点位
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Setter
+@Getter
+@TableName(value = "CONFIG_DATA_POINT", resultMap = "ConfigDataPointWithEA")
+public class ConfigDataPoint extends BaseDataPoint {
+
+
+    /**
+     * 数值默认值
+     */
+    private BigDecimal defaultValue;
+
+    public ConfigDataPoint() {
+        this.dataPointType = DataPointType.CONFIG;
+    }
+
+
+}

+ 48 - 0
src/main/java/entity/datapoint/GatherDataPoint.java

@@ -0,0 +1,48 @@
+package entity.datapoint;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 采集点表
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Getter
+@Setter
+@TableName(value = "GATHER_DATA_POINT", resultMap = "GatherDataPointWithEA")
+public class GatherDataPoint extends BaseDataPoint {
+
+    /**
+     * 功能码
+     */
+    Integer functionCode;
+
+
+    /**
+     * 倍率
+     */
+    private BigDecimal ratio;
+
+
+    /**
+     * 所属通道
+     */
+    private String tunnelId;
+
+
+    /**
+     * 通道点位
+     */
+    private Integer protocolPoint;
+
+    public GatherDataPoint() {
+        this.dataPointType = DataPointType.GATHER;
+    }
+
+
+}

+ 59 - 0
src/main/java/entity/datapoint/SenderDataPoint.java

@@ -0,0 +1,59 @@
+package entity.datapoint;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Getter;
+import lombok.Setter;
+import enums.DataType;
+import java.math.BigDecimal;
+
+
+
+/**
+ * 转发点表
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Setter
+@Getter
+@TableName(value = "SENDER_DATA_POINT")
+public class SenderDataPoint {
+
+
+	@TableId(type = IdType.AUTO)
+	private Integer id;
+
+	/**
+	 * 所属通道
+	 */
+	private String tunnelId;
+
+	/**
+	 * 通道点位
+	 */
+	private Integer protocolPoint;
+
+	/**
+	 * 采集点表的ID 即数据池的ID
+	 */
+	private String dataPointId;
+
+	/**
+	 * 协议数据类型
+	 */
+	@EnumValue
+	@JsonFormat(shape = JsonFormat.Shape.STRING)
+	private DataType dataType;
+
+
+	/**
+	 * 倍率
+	 */
+	private BigDecimal ratio;
+
+
+}

+ 47 - 0
src/main/java/enums/CommandProtocolType.java

@@ -0,0 +1,47 @@
+package enums;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 协议命令类型
+ *
+ * @author xiuwei
+ * @date 2021/07/12
+ */
+@AllArgsConstructor
+@Getter
+@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum CommandProtocolType {
+	WRITE_COIL(5, TunnelType.MODBUS, "写单个线圈"),
+	WRITE_REGISTER(6, TunnelType.MODBUS, "写单个寄存器"),
+	WRITE_COILS(15, TunnelType.MODBUS, "写连续线圈"),
+	WRITE_REGISTERS(16, TunnelType.MODBUS, "写连续寄存器"),
+	SINGLE_BOOLEAN(45, TunnelType.IEC104, "单点遥控"),
+	DOUBLE_BOOLEAN(46, TunnelType.IEC104, "双点遥控"),
+	NORMALIZATION(48, TunnelType.IEC104, "归一化值遥调"),
+	SHORT_FLOAT(50, TunnelType.IEC104, "浮点遥调");
+
+
+	/**
+	 * 代码
+	 */
+	private Integer code;
+	/**
+	 * 通道类型
+	 */
+	private TunnelType tunnelType;
+	/**
+	 * 解释
+	 */
+	private String explain;
+
+	public enum TunnelType {
+		MODBUS, IEC104, CDT
+	}
+
+	public String getKey() {
+		return this.name();
+	}
+}

+ 116 - 0
src/main/java/enums/DataType.java

@@ -0,0 +1,116 @@
+package enums;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import wei.yigulu.modbus.domain.datatype.ModbusDataTypeEnum;
+
+/**
+ * 协议中的集中数据类型
+ *
+ * @author xiuwei
+ */
+@AllArgsConstructor
+@Getter
+@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum DataType {
+
+	/**
+	 * 列举几种数据类型
+	 * 开关量  遥信值
+	 */
+	A16(0, "A", 2, "开关量", ModbusDataTypeEnum.A16),
+
+	/**
+	 * 无符号整型仅正数
+	 */
+	P_AB(1, "+AB", 2, "无符号整型仅正数 0到65535", ModbusDataTypeEnum.P_AB),
+	/**
+	 * 有符号整型可正负
+	 */
+	PM_AB(2, "±AB", 2, "有符号整型可正负 -32768到32765", ModbusDataTypeEnum.PM_AB),
+	/**
+	 * 无符号高低转换整型仅正数
+	 */
+	P_BA(3, "+BA", 2, "无符号高低转换整型仅正数 0到65535", ModbusDataTypeEnum.P_BA),
+	/**
+	 * 有符号高低转换整型可正负
+	 */
+	PM_BA(4, "±BA", 2, "有符号高低转换整型可正负 -32768到32765", ModbusDataTypeEnum.PM_BA),
+	/**
+	 * 无符号四字节整型仅正数
+	 */
+	P_AABB(5, "+AABB", 4, "无符号四字节整型仅正数 0到4294967295", ModbusDataTypeEnum.P_ABCD),
+	/**
+	 * 有符号四字节整型可正负
+	 */
+	PM_AABB(6, "±AABB", 4, "有符号四字节整型可正负 -2147483648到2147483647", ModbusDataTypeEnum.PM_ABCD),
+	/**
+	 * 无符号高低转换四字节整型仅正数
+	 */
+	P_BBAA(7, "+BBAA", 4, "无符号高低转换四字节整型仅正数 0到4294967295", ModbusDataTypeEnum.P_CDAB),
+	/**
+	 * 有符号高低转换四字节整型可正负
+	 */
+	PM_BBAA(8, "±BBAA", 4, "有符号高低转换四字节整型可正负 -2147483648到2147483647", ModbusDataTypeEnum.PM_CDAB),
+	/**
+	 * 四字节短浮点
+	 */
+	ABCD(9, "ABCD", 4, "四字节短浮点", ModbusDataTypeEnum.ABCD),
+
+	/**
+	 * 四字节高低位转换浮点
+	 */
+	CDAB(10, "CDAB", 4, "四字节高低位转换浮点", ModbusDataTypeEnum.CDAB),
+	/**
+	 * 四字节高低位转换反转浮点
+	 */
+	DCBA(11, "DCBA", 4, "四字节高低位转换反转浮点", ModbusDataTypeEnum.DCBA),
+
+
+	/**
+	 * 遥测值 是下面 这些遥测类型的总称 用于不区分遥测类型的情况
+	 */
+	YX(12, "YX", 0, "开关量", ModbusDataTypeEnum.A16),
+
+	/**
+	 * 遥测值 是下面 这些遥测类型的总称 用于不区分遥测类型的情况
+	 */
+	YC(12, "YC", 4, "数值量", ModbusDataTypeEnum.ABCD),
+
+	/**
+	 * 四字节短浮点
+	 */
+	BADC(13, "BADC", 4, "四字节短浮点", ModbusDataTypeEnum.BADC);
+
+
+	/**
+	 * 数据类型代号
+	 */
+	private Integer code;
+
+	/**
+	 * 数据类型的表达式
+	 */
+	private String describe;
+
+
+	/**
+	 * 占用点位
+	 */
+	private Integer occupiedBytes;
+
+	/**
+	 * 数据类型描述
+	 */
+	private String cnDescribe;
+
+	/**
+	 * 对应modbus中的数据类型
+	 */
+	private ModbusDataTypeEnum modbusDataType;
+
+	public String getKey() {
+		return this.name();
+	}
+}

+ 69 - 0
src/main/java/enums/EquipmentTypeEnum.java

@@ -0,0 +1,69 @@
+package enums;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 设备数据对象类型枚举
+ * lastModify
+ * 2020.3.26  修唯   枚举名修改,新增发电站类型,删除各种功率,设备数,开机容量等类型
+ * @version 1.0
+ * @since 2019/8/19 8:59
+ */
+@Getter
+@AllArgsConstructor
+//@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum EquipmentTypeEnum {
+
+    /**
+     * 场站也视为设备类型的一种
+     */
+    POWERSTATION(0, "发电站"),
+    SVG(1, "SVG/SVC"),
+    INVERTER(2, "逆变器"),
+    WINDTOWER(3, "测风塔"),
+    WINDTURBINE(4, "风机"),
+    EMS(5, "能管平台"),
+    BOOSTERSTATION(6, "升压站"),
+    AGC(7, "AGC"),
+    AVC(8, "AVC"),
+    COLLECTIONLINE(9, "集电线");
+
+
+    private Integer code;
+
+    private String message;
+
+
+    public static EquipmentTypeEnum valueOf(Integer code) {
+        switch (code) {
+            case 0:
+                return POWERSTATION;
+            case 1:
+                return SVG;
+            case 2:
+                return INVERTER;
+            case 4:
+                return WINDTURBINE;
+            case 5:
+                return EMS;
+            case 6:
+                return BOOSTERSTATION;
+            case 7:
+                return AGC;
+            case 8:
+                return AVC;
+            case 9:
+                return COLLECTIONLINE;
+            default:
+        }
+        return null;
+    }
+
+
+
+    public String getKey() {
+        return this.name();
+    }
+}

+ 136 - 0
src/main/java/enums/TunnelStatus.java

@@ -0,0 +1,136 @@
+package enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 通道的状态
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@Getter
+@AllArgsConstructor
+public enum TunnelStatus {
+
+
+    /**
+     * 通道状态枚举
+     */
+    BUILT(0, "已创建"),
+    /**
+     * 已连接
+     */
+    CONNECTED(1, "已连接"),
+    /**
+     * 被动丢失连接
+     */
+    LOSECONN(2, "被动丢失连接"),
+    /**
+     * 通道已主动关闭
+     */
+    ACTIVECLOSE(3, "通道已主动关闭"),
+    /**
+     * 正在尝试连接
+     */
+    CONNECTING(4, "正在尝试连接"),
+
+
+    /**
+     * listeningport 监听网口成功
+     */
+    LISTENPORTSUCCESS(5, "监听端口成功"),
+
+    /**
+     * listeningSerial 监听串口成功
+     */
+    LISTENSERIALSUCCESS(6, "监听串口成功"),
+
+
+    /**
+     * listeningport 监听网口失败
+     */
+    LISTENPORTFAIL(7, "监听端口失败"),
+
+    /**
+     * listeningSerial 监听串口失败
+     */
+    LISTENSERIALFAIL(8, "监听串口失败"),
+
+    /**
+     * listeningport 正在监听网口
+     */
+    LISTENINGPORT(9, "正在监听端口"),
+
+    /**
+     * listeningSerial 正在监听串口
+     */
+    LISTENINGSERIAL(10, "正在监听串口"),
+
+    /**
+     * listeningport 监听网口成功,并有客户端连接
+     */
+    LISTENPORTSUCCESSANDCONN(13, "监听端口成功并有客户端连接"),
+
+    /**
+     * listeningport 监听网口成功,并无客户端连接
+     */
+    LISTENPORTSUCCESSANDNOCONN(14, "监听端口成功并无客户端连接"),
+
+    /**
+     * listeningport 监听网口成功,并有客户端连接,且交互异常
+     */
+    LISTENPORTSUCCESSANDCONNANDCOMMERROR(15, "监听端口成功并有客户端连接但交互异常"),
+
+    /**
+     * listeningSerial 监听串口成功但交互异常
+     */
+    LISTENSERIALSUCCESSANDCOMMERROR(16, "监听串口成功但交互异常"),
+
+
+    /**
+     * 已连接,但是交互异常
+     */
+    CONNECTEDANDCOMMERROR(17, "已连接但交互异常");
+
+
+    /**
+     * 可用状态
+     */
+    public static final List<TunnelStatus> ABLE_STATUS = Arrays.asList(
+            TunnelStatus.LISTENSERIALSUCCESS,//监听串口正常
+            TunnelStatus.LISTENPORTSUCCESSANDCONN, //监听端口成功,并有连接
+            TunnelStatus.LISTENPORTSUCCESSANDCONNANDCOMMERROR,//监听端口成功,并有连接且交互异常
+            TunnelStatus.LISTENSERIALSUCCESSANDCOMMERROR,//监听串口正常 且交互异常
+            TunnelStatus.LISTENPORTSUCCESS,//监听端口成功
+            TunnelStatus.CONNECTED,//客户端 成功连接端
+            TunnelStatus.CONNECTEDANDCOMMERROR//客户端 成功连接端 但交互异常
+    );
+
+
+    /**
+     * 正确的状态
+     */
+    public static final List<TunnelStatus> CORRECT_STATUS = Arrays.asList(
+            TunnelStatus.LISTENSERIALSUCCESS,//监听串口正常
+            TunnelStatus.LISTENPORTSUCCESSANDCONN, //监听端口成功,并有连接
+            TunnelStatus.CONNECTED//客户端 成功连接端
+    );
+
+
+    /**
+     * 通道的类型代号
+     */
+    private Integer code;
+
+
+    /**
+     * 通道的类型描述
+     */
+    private String cnDescribe;
+
+
+}

+ 86 - 0
src/main/java/enums/TunnelType.java

@@ -0,0 +1,86 @@
+package enums;
+
+
+import entity.BaseTunnelInfo;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import mastertunnelinfo.Master104TcpTunnelInfo;
+import mastertunnelinfo.MasterCdtRtuTunnelInfo;
+import mastertunnelinfo.MasterModbusRtuTunnelInfo;
+import mastertunnelinfo.MasterModbusTcpTunnelInfo;
+import slavertunnelinfo.Slaver104TcpTunnelInfo;
+import slavertunnelinfo.SlaverCdtRtuTunnelInfo;
+import slavertunnelinfo.SlaverModbusRtuTunnelInfo;
+import slavertunnelinfo.SlaverModbusTcpTunnelInfo;
+
+/**
+ * 通道的类型
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+
+@AllArgsConstructor
+@Getter
+//@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+public enum TunnelType {
+
+
+	/**
+	 * 通道类型枚举
+	 */
+	MODBUSTCPMASTER("modbusTCP采集", "in", MasterModbusTcpTunnelInfo.class),
+	/**
+	 * modbusRTU采集
+	 */
+	MODBUSRTUMASTER("modbusRTU采集", "in", MasterModbusRtuTunnelInfo.class),
+	/**
+	 * IEC104TCP采集
+	 */
+	IEC104TCPMASTER("IEC104TCP采集", "in", Master104TcpTunnelInfo.class),
+	/**
+	 * CDTRTU采集
+	 */
+	CDTRTUMASTER("CDTRTU采集", "in", MasterCdtRtuTunnelInfo.class),
+	/**
+	 * modbusTCP转发
+	 */
+	MODBUSTCPSLAVE("modbusTCP转发", "out", SlaverModbusTcpTunnelInfo.class),
+	/**
+	 * modbusRTU转发
+	 */
+	MODBUSRTUSLAVE("modbusRTU转发", "out", SlaverModbusRtuTunnelInfo.class),
+	/**
+	 * IEC104TCP转发
+	 */
+	IEC104TCPSLAVE("IEC104TCP转发", "out", Slaver104TcpTunnelInfo.class),
+	/**
+	 * CDTRTU转发
+	 */
+	CDTRTUSLAVE("CDTRTU转发", "out", SlaverCdtRtuTunnelInfo.class);
+
+
+	/**
+	 * 通道的类型描述
+	 */
+
+	private String cnDescribe;
+
+
+	/**
+	 * 流向
+	 */
+	private String flow;
+
+	/**
+	 * 对应通道实体类
+	 */
+	//@JsonIgnore
+	private Class<? extends BaseTunnelInfo> clazz;
+
+	public String getKey() {
+		return this.name();
+	}
+
+
+}

+ 23 - 0
src/main/java/exception/DataExchangeException.java

@@ -0,0 +1,23 @@
+package exception;
+
+/**
+ * 数据交互模块的异常
+ *
+ * @author: xiuwei
+ * @version:
+ */
+public class DataExchangeException extends Exception {
+
+    final Integer code;
+    final String msg;
+
+    public DataExchangeException(Integer code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public DataExchangeException(String msg) {
+        this.code = 0;
+        this.msg = msg;
+    }
+}

+ 81 - 0
src/main/java/logger/TunnelLoggerFactory.java

@@ -0,0 +1,81 @@
+package logger;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
+import ch.qos.logback.core.rolling.RollingFileAppender;
+import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;
+import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
+import ch.qos.logback.core.util.FileSize;
+import org.slf4j.LoggerFactory;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * 通道logger的工厂  仅针对在slf4j 日志框架下 logback 实现
+ * 修改 日志框架或实现时均需修改该类
+ *
+ * @author: xiuwei
+ * @version:
+ */
+public class TunnelLoggerFactory {
+
+
+	public static Logger getLogger(String tunnelId, String tunnelName) {
+		//使用在slf4j的接口框架下获取 logback的 LoggerContext
+		LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+
+		Logger logger = loggerContext.getLogger(tunnelId);
+		logger.setLevel(Level.DEBUG);
+
+		//构建编码规则
+		PatternLayoutEncoder encoder = new PatternLayoutEncoder();
+		encoder.setContext(loggerContext);
+		encoder.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level  %M-%L  - %msg%n");
+		encoder.start();
+
+		//构建appender
+		RollingFileAppender appender = new RollingFileAppender();
+		appender.setContext(loggerContext);
+
+
+		//构建滚动规则
+		TimeBasedRollingPolicy rollingPolicyBase = new TimeBasedRollingPolicy<>();
+		rollingPolicyBase.setContext(loggerContext);
+		rollingPolicyBase.setParent(appender);
+		try {
+			rollingPolicyBase.setFileNamePattern("./logs/" + "%d{yyyy-MM-dd}" + "/" +new String(tunnelName.getBytes(),"UTF-8")  + ".%d{yyyy-MM-dd}.%i.log.gz");
+		} catch (UnsupportedEncodingException e) {
+			rollingPolicyBase.setFileNamePattern("./logs/" + "%d{yyyy-MM-dd}" + "/" +tunnelName+ ".%d{yyyy-MM-dd}.%i.log.gz");
+		}
+		//设定滚动触发器
+		SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP();
+		sizeAndTimeBasedFNATP.setMaxFileSize(FileSize.valueOf("1GB"));
+		rollingPolicyBase.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFNATP);
+		//最大文件数
+		rollingPolicyBase.setMaxHistory(30);
+		rollingPolicyBase.setCleanHistoryOnStart(true);
+		rollingPolicyBase.setTotalSizeCap(FileSize.valueOf("3GB"));
+		rollingPolicyBase.start();
+
+		appender.setEncoder(encoder);
+		appender.setRollingPolicy(rollingPolicyBase);
+		appender.start();
+
+		logger.setAdditive(false);
+		logger.addAppender(appender);
+		return logger;
+	}
+
+	/**
+	 * 关闭该logger
+	 *
+	 * @param logger
+	 */
+	public static void stop(Logger logger) {
+		LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
+		loggerContext.removeObject(logger.getName());
+		logger.detachAndStopAllAppenders();
+	}
+}

+ 35 - 0
src/main/java/mastertunnelinfo/Master104TcpTunnelInfo.java

@@ -0,0 +1,35 @@
+package mastertunnelinfo;
+
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import entity.BaseTcpMasterTunnelInfo;
+import enums.TunnelType;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 104协议tcp数据采集通道信息
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@TableName(value = "TCP_MASTER_TUNNEL_INFO")
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class Master104TcpTunnelInfo extends BaseTcpMasterTunnelInfo {
+
+
+    /**
+     * 公共地址位
+     */
+    private Integer publicAddress;
+
+    /**
+     * 初始化时确定通道类型
+     */
+    public Master104TcpTunnelInfo() {
+        this.setTunnelType(TunnelType.IEC104TCPMASTER);
+    }
+
+
+}

+ 31 - 0
src/main/java/mastertunnelinfo/MasterCdtRtuTunnelInfo.java

@@ -0,0 +1,31 @@
+package mastertunnelinfo;
+
+
+
+import entity.BaseRtuTunnelInfo;
+import entity.datapoint.GatherDataPoint;
+import enums.TunnelType;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * cdt协议rtu通道信息
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+//@TableName(value = "RTU_TUNNEL_INFO")
+public class MasterCdtRtuTunnelInfo extends BaseRtuTunnelInfo<GatherDataPoint> {
+
+
+	/**
+	 * 初始化时确定通道类型
+	 */
+	public MasterCdtRtuTunnelInfo() {
+		this.setTunnelType(TunnelType.CDTRTUMASTER);
+	}
+
+
+}

+ 37 - 0
src/main/java/mastertunnelinfo/MasterModbusRtuTunnelInfo.java

@@ -0,0 +1,37 @@
+package mastertunnelinfo;
+
+
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import entity.BaseRtuTunnelInfo;
+import entity.datapoint.GatherDataPoint;
+import enums.TunnelType;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * modbus协议rtu采集通道
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@TableName(value = "RTU_TUNNEL_INFO")
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class MasterModbusRtuTunnelInfo extends BaseRtuTunnelInfo<GatherDataPoint> {
+
+
+	/**
+	 * 设备地址
+	 */
+	private Integer slaveId;
+
+	/**
+	 * 初始化时确定通道类型
+	 */
+	public MasterModbusRtuTunnelInfo() {
+		this.setTunnelType(TunnelType.MODBUSRTUMASTER);
+	}
+
+
+}

+ 36 - 0
src/main/java/mastertunnelinfo/MasterModbusTcpTunnelInfo.java

@@ -0,0 +1,36 @@
+package mastertunnelinfo;
+
+
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import entity.BaseTcpMasterTunnelInfo;
+import enums.TunnelType;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * modbus协议tcp采集通道
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@TableName(value = "TCP_MASTER_TUNNEL_INFO")
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class MasterModbusTcpTunnelInfo extends BaseTcpMasterTunnelInfo {
+
+	/**
+	 * 初始化时确定通道类型
+	 */
+	public MasterModbusTcpTunnelInfo() {
+		this.setTunnelType(TunnelType.MODBUSTCPMASTER);
+	}
+
+
+	/**
+	 * 设备地址
+	 */
+	private Integer slaveId;
+
+
+}

+ 37 - 0
src/main/java/slavertunnelinfo/Slaver104TcpTunnelInfo.java

@@ -0,0 +1,37 @@
+package slavertunnelinfo;
+
+
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import entity.BaseTcpSlaverTunnelInfo;
+import enums.TunnelType;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 104协议tcp发送数据通道信息
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@TableName(value = "TCP_SLAVER_TUNNEL_INFO")
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class Slaver104TcpTunnelInfo extends BaseTcpSlaverTunnelInfo {
+
+
+	/**
+	 * 初始化时确定通道类型
+	 */
+	public Slaver104TcpTunnelInfo() {
+		this.setTunnelType(TunnelType.IEC104TCPSLAVE);
+	}
+
+
+	/**
+	 * 公共地址位
+	 */
+	private Integer publicAddress;
+
+
+}

+ 30 - 0
src/main/java/slavertunnelinfo/SlaverCdtRtuTunnelInfo.java

@@ -0,0 +1,30 @@
+package slavertunnelinfo;
+
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import entity.BaseRtuTunnelInfo;
+import entity.datapoint.SenderDataPoint;
+import enums.TunnelType;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * cdt协议rtu发送数据通道信息
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "RTU_TUNNEL_INFO")
+public class SlaverCdtRtuTunnelInfo extends BaseRtuTunnelInfo<SenderDataPoint> {
+
+	/**
+	 * 初始化时确定通道类型
+	 */
+	public SlaverCdtRtuTunnelInfo() {
+		this.setTunnelType(TunnelType.CDTRTUSLAVE);
+	}
+
+
+}

+ 36 - 0
src/main/java/slavertunnelinfo/SlaverModbusRtuTunnelInfo.java

@@ -0,0 +1,36 @@
+package slavertunnelinfo;
+
+
+
+import entity.BaseRtuTunnelInfo;
+import entity.datapoint.SenderDataPoint;
+import enums.TunnelType;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * modbus协议rtu发送数据通道信息
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+//@TableName(value = "RTU_TUNNEL_INFO")
+public class SlaverModbusRtuTunnelInfo extends BaseRtuTunnelInfo<SenderDataPoint> {
+
+
+    /**
+     * 设备地址
+     */
+    private Integer slaveId;
+
+    /**
+     * 初始化时确定通道类型
+     */
+    public SlaverModbusRtuTunnelInfo() {
+        this.setTunnelType(TunnelType.MODBUSRTUSLAVE);
+    }
+
+
+}

+ 36 - 0
src/main/java/slavertunnelinfo/SlaverModbusTcpTunnelInfo.java

@@ -0,0 +1,36 @@
+package slavertunnelinfo;
+
+
+
+import entity.BaseTcpSlaverTunnelInfo;
+import enums.TunnelType;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * modbus协议tcp发送数据通道信息
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+//@TableName(value = "TCP_SLAVER_TUNNEL_INFO")
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SlaverModbusTcpTunnelInfo extends BaseTcpSlaverTunnelInfo {
+
+
+	/**
+	 * 设备地址
+	 */
+	private Integer slaveId;
+
+
+	/**
+	 * 创建类时确定通道类型
+	 */
+	public SlaverModbusTcpTunnelInfo() {
+		this.setTunnelType(TunnelType.MODBUSTCPSLAVE);
+	}
+
+
+}

+ 145 - 0
src/main/java/tunnelworker/BaseProtocolTunnel.java

@@ -0,0 +1,145 @@
+package tunnelworker;
+
+import ch.qos.logback.classic.Logger;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONField;
+import container.ProtocolTunnelContainer;
+import entity.BaseTunnelInfo;
+import entity.datapoint.CommandDataPoint;
+import enums.TunnelStatus;
+import enums.TunnelType;
+import exception.DataExchangeException;
+import logger.TunnelLoggerFactory;
+import lombok.Getter;
+import lombok.Setter;
+import wei.yigulu.modbus.exceptiom.ModbusException;
+import wei.yigulu.netty.BaseProtocolBuilder;
+
+import java.net.InetSocketAddress;
+import java.util.List;
+
+/**
+ * 通道信息的抽象类,用以实时储存、管理、改变、查询通道的状态
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@Getter
+@Setter
+public abstract class BaseProtocolTunnel<T extends BaseTunnelInfo, E extends BaseProtocolBuilder> {
+
+
+    protected Logger log;
+
+
+    protected E protocolBuilder;
+    /**
+     * 该通道的信息及其负责采集的点位
+     */
+    @JSONField(serialize = false)
+    protected T tunnelInfo;
+    /**
+     * 通道的ID
+     */
+    protected String tunnelId;
+    /**
+     * 通道的类型
+     */
+    protected TunnelType tunnelType;
+    /**
+     * 连接接信息
+     */
+    @JSONField(serialize = false)
+    protected List<InetSocketAddress> connectors;
+    /**
+     * 通道容器
+     */
+    protected ProtocolTunnelContainer protocolTunnelContainer = ProtocolTunnelContainer.getInstance();
+    /**
+     * 通道的状态
+     */
+    private TunnelStatus tunnelStatus;
+
+    /**
+     * 顶层的构造方法
+     *
+     * @param tunnelInfo 通道信息
+     */
+    public BaseProtocolTunnel(T tunnelInfo) {
+        this.tunnelInfo = tunnelInfo;
+        this.tunnelType = tunnelInfo.getTunnelType();
+        this.tunnelId = tunnelInfo.getId();
+        this.log = TunnelLoggerFactory.getLogger(tunnelId, tunnelInfo.getTunnelName());
+    }
+
+    /**
+     * 开启通道通讯  协议角度  创建socket连接
+     *
+     * @return AbstractDataGather abstract data gather
+     * @throws DataExchangeException 抛出自定义的异常
+     */
+    public abstract BaseProtocolTunnel startTunnel() throws DataExchangeException;
+
+
+    public BaseProtocolTunnel build() throws DataExchangeException {
+        buildTunnel();
+        parseDataPoint();
+        return this;
+    }
+
+    /**
+     * 创建不同协议对应的通道
+     *
+     * @return AbstractDataGather abstract data gather
+     * @throws DataExchangeException 抛出自定义的异常
+     */
+    protected abstract BaseProtocolTunnel buildTunnel() throws DataExchangeException;
+
+
+    /**
+     * 更新通道信息
+     *
+     * @param tunnelInfo 通道信息
+     * @return 实体本身
+     */
+    public BaseProtocolTunnel refreshTunnelInfo(T tunnelInfo) {
+        log.info("刷新{}的通道信息及点位", tunnelInfo.getTunnelName());
+        this.tunnelInfo = tunnelInfo;
+        this.setLog(TunnelLoggerFactory.getLogger(tunnelId, tunnelInfo.getTunnelName()));
+        return this;
+    }
+
+    /**
+     * 解析采集数据点表
+     *
+     * @throws DataExchangeException 自定义异常
+     */
+    protected abstract void parseDataPoint() throws DataExchangeException, DataExchangeException;
+
+
+    /**
+     * 通道停止
+     * 停止 关闭通道
+     *
+     * @return {@link BaseProtocolTunnel}
+     * @throws DataExchangeException 数据交换异常
+     */
+    public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
+        setTunnelStatus(TunnelStatus.ACTIVECLOSE);
+        protocolTunnelContainer.stopUpdateTask(this.tunnelId);
+        TunnelLoggerFactory.stop(log);
+        return this;
+    }
+
+
+    public JSONObject getJSONObj() {
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("tunnelId", this.tunnelId);
+        jsonObject.put("tunnelName", this.tunnelInfo.getTunnelName());
+        jsonObject.put("tunnelType", this.tunnelType);
+        jsonObject.put("tunnelStatus", this.tunnelStatus);
+        return jsonObject;
+    }
+
+
+}

+ 35 - 0
src/main/java/tunnelworker/masters/MasterInterface.java

@@ -0,0 +1,35 @@
+package tunnelworker.masters;
+
+
+import entity.datapoint.CommandDataPoint;
+import exception.DataExchangeException;
+
+/**
+ * 通过接口采集数据的抽象类
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+public interface MasterInterface {
+
+
+    /**
+     * 该通道通过自身协议采集负责点位的数据并存入缓存池
+     *
+     * @throws DataExchangeException 自定义异常
+     */
+    void getData() throws DataExchangeException;
+
+
+    /**
+     * 发送命令
+     *
+     * @param dataPoint 数据点
+     * @param val       值
+     * @return boolean* @throws DataExchangeException 数据交换异常
+     * @throws Exception 异常
+     */
+    boolean sendCommand(CommandDataPoint dataPoint, Object val) throws Exception;
+
+
+}

+ 236 - 0
src/main/java/tunnelworker/masters/iml/BaseModbusMaster.java

@@ -0,0 +1,236 @@
+package tunnelworker.masters.iml;
+
+
+import container.ProtocolDataContainer;
+import entity.BaseTunnelInfo;
+import entity.datapoint.CommandDataPoint;
+import entity.datapoint.GatherDataPoint;
+import exception.DataExchangeException;
+import lombok.Getter;
+import lombok.Setter;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.masters.MasterInterface;
+import wei.yigulu.modbus.domain.FunctionCode;
+import wei.yigulu.modbus.domain.Obj4RequestCoil;
+import wei.yigulu.modbus.domain.Obj4RequestRegister;
+import wei.yigulu.modbus.domain.datatype.BooleanModbusDataInRegister;
+import wei.yigulu.modbus.domain.datatype.IModbusDataType;
+import wei.yigulu.modbus.domain.datatype.ModbusDataTypeEnum;
+import wei.yigulu.modbus.domain.datatype.NumericModbusData;
+import wei.yigulu.modbus.exceptiom.ModbusException;
+import wei.yigulu.modbus.utils.ModbusCommandDataUtils;
+import wei.yigulu.modbus.utils.ModbusRequestDataUtils;
+import wei.yigulu.netty.AbstractMasterBuilder;
+import wei.yigulu.netty.BaseProtocolBuilder;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static wei.yigulu.modbus.domain.FunctionCode.WRITE_REGISTER;
+
+/**
+ * modbus数据处理的基类
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Getter
+@Setter
+public abstract class BaseModbusMaster<T extends BaseTunnelInfo, E extends BaseProtocolBuilder> extends BaseProtocolTunnel<T, E> implements MasterInterface {
+
+    /**
+     * 通道点位  ---- 寄存器 功能码3 点位对象
+     */
+    private Map<Integer, GatherDataPoint> r3DataPointMap;
+
+
+    /**
+     * 通道点位  ---- 寄存器 功能码4 点位对象
+     */
+    private Map<Integer, GatherDataPoint> r4DataPointMap;
+
+
+    /**
+     * 通道点位  ---- 线圈 功能码1 点位对象
+     */
+    private Map<Integer, GatherDataPoint> c1DataPointMap;
+
+
+    /**
+     * 通道点位  ---- 线圈  功能码2 点位对象
+     */
+    private Map<Integer, GatherDataPoint> c2DataPointMap;
+
+
+    /**
+     * 向modbus slave 功能码3 端发送的请求
+     */
+    private List<Obj4RequestRegister> requestRegister3List;
+
+    /**
+     * 向modbus slave 功能码4 端发送的请求
+     */
+    private List<Obj4RequestRegister> requestRegister4List;
+
+
+    /**
+     * 向modbus slave 功能码1 端发送线圈的请求
+     */
+    private List<Obj4RequestCoil> requestCoil1List;
+
+    /**
+     * 向modbus slave 功能码2 端发送线圈的请求
+     */
+    private List<Obj4RequestCoil> requestCoil2List;
+
+    /**
+     * 设备地址
+     */
+    private Integer slaveId = 1;
+
+
+    /**
+     * 顶层的构造方法
+     *
+     * @param tunnelInfo 通道信息
+     */
+    public BaseModbusMaster(T tunnelInfo) {
+        super(tunnelInfo);
+    }
+
+
+    @Override
+    protected void parseDataPoint() throws DataExchangeException {
+        log.info("解析该通道下所管理的点位");
+        List<GatherDataPoint> dataPoints = this.tunnelInfo.getDataPoints();
+        if (dataPoints == null || dataPoints.size() == 0) {
+            return;
+        }
+        Map<Integer, ModbusDataTypeEnum> r3Points = new HashMap<>();
+        Map<Integer, ModbusDataTypeEnum> r4Points = new HashMap<>();
+        List<Integer> coil1Points = new ArrayList<>();
+        List<Integer> coil2Points = new ArrayList<>();
+        this.r3DataPointMap = new HashMap<>();
+        this.r4DataPointMap = new HashMap<>();
+        this.c1DataPointMap = new HashMap<>();
+        this.c2DataPointMap = new HashMap<>();
+        for (GatherDataPoint d : dataPoints) {
+            if (d.getFunctionCode() == null) {
+                continue;
+            }
+            switch (d.getFunctionCode()) {
+                case 1:
+                    this.c1DataPointMap.put(d.getProtocolPoint(), d);
+                    coil1Points.add(d.getProtocolPoint());
+                    break;
+                case 2:
+                    this.c2DataPointMap.put(d.getProtocolPoint(), d);
+                    coil2Points.add(d.getProtocolPoint());
+                    break;
+                case 3:
+                    this.r3DataPointMap.put(d.getProtocolPoint(), d);
+                    r3Points.put(d.getProtocolPoint(), d.getDataType().getModbusDataType());
+                    break;
+                case 4:
+                    this.r4DataPointMap.put(d.getProtocolPoint(), d);
+                    r4Points.put(d.getProtocolPoint(), d.getDataType().getModbusDataType());
+                    break;
+                default:
+                    throw new DataExchangeException("Modbus 仅能采集功能码为1,2,3,4的数据");
+            }
+        }
+        try {
+            if (this.c1DataPointMap.size() > 0) {
+                this.requestCoil1List = ModbusRequestDataUtils.splitModbusRequest(coil1Points, getSlaveId(), FunctionCode.valueOf(1));
+            }
+            if (this.c2DataPointMap != null && this.c2DataPointMap.size() > 0) {
+                this.requestCoil2List = ModbusRequestDataUtils.splitModbusRequest(coil2Points, getSlaveId(), FunctionCode.valueOf(2));
+            }
+            if (this.r3DataPointMap != null && this.r3DataPointMap.size() > 0) {
+                this.requestRegister3List = ModbusRequestDataUtils.splitModbusRequest(r3Points, getSlaveId(), FunctionCode.valueOf(3));
+            }
+            if (this.r4DataPointMap != null && this.r4DataPointMap.size() > 0) {
+                this.requestRegister4List = ModbusRequestDataUtils.splitModbusRequest(r4Points, getSlaveId(), FunctionCode.valueOf(4));
+            }
+        } catch (ModbusException e) {
+            throw new DataExchangeException(e.getCode(), e.getMsg());
+        }
+        log.info("解析该通道下所管理的点位完成");
+    }
+
+
+    @Override
+    public boolean sendCommand(CommandDataPoint dataPoint, Object val) throws DataExchangeException, ModbusException {
+        log.debug("通道{}发送控制命令,命令类型{},控制值{}",tunnelInfo.getTunnelName(),dataPoint.getCommandProtocolType().getExplain(),val);
+        log.debug("当下通道状态:{}",getTunnelStatus());
+        IModbusDataType c = dataPoint.getDataType().getModbusDataType().getObject();
+        switch (dataPoint.getCommandProtocolType()) {
+            case WRITE_REGISTER:
+                if (c instanceof NumericModbusData) {
+                    return ModbusCommandDataUtils.commandRegister((wei.yigulu.netty.MasterInterface) this.protocolBuilder, this.slaveId, dataPoint.getProtocolPoint(), ((NumericModbusData) c).setValue(((BigDecimal) val).multiply(dataPoint.getRatio())));
+                } else {
+                    BooleanModbusDataInRegister b = new BooleanModbusDataInRegister();
+                    b.setValue(0, (Boolean) val);
+                    return ModbusCommandDataUtils.commandRegister((wei.yigulu.netty.MasterInterface) this.protocolBuilder, this.slaveId, dataPoint.getProtocolPoint(), b);
+                }
+            case WRITE_COIL:
+                return ModbusCommandDataUtils.commandCoil((wei.yigulu.netty.MasterInterface) this.protocolBuilder, this.slaveId, dataPoint.getProtocolPoint(), (Boolean) val);
+            default:
+                return false;
+        }
+    }
+
+
+    @Override
+    public void getData() throws DataExchangeException {
+        log.info("获取到从通道中获取的点位");
+        try {
+            extractRegisterData(r3DataPointMap, requestRegister3List);
+            extractRegisterData(r4DataPointMap, requestRegister4List);
+            extractCoilData(c2DataPointMap, requestCoil2List);
+            extractCoilData(c1DataPointMap, requestCoil1List);
+        } catch (ModbusException e) {
+            throw new DataExchangeException(e.getCode(), e.getMsg());
+        }
+    }
+
+    /**
+     * 提取寄存器数据
+     *
+     * @param rDataPointMap       数据点映射
+     * @param requestRegisterList 请求寄存器列表
+     * @throws ModbusException modbus例外
+     */
+    private void extractRegisterData(Map<Integer, GatherDataPoint> rDataPointMap, List<Obj4RequestRegister> requestRegisterList) throws ModbusException {
+        if (requestRegisterList != null) {
+            Map<Integer, IModbusDataType> map = ModbusRequestDataUtils.getRegisterData((AbstractMasterBuilder) this.protocolBuilder, requestRegisterList);
+            for (Map.Entry<Integer, IModbusDataType> i : map.entrySet()) {
+                if (i.getValue() instanceof NumericModbusData) {
+                    ProtocolDataContainer.getInstance().putGNumber(rDataPointMap.get(i.getKey()).getId(), ((NumericModbusData) i.getValue()).getValue().multiply(rDataPointMap.get(i.getKey()).getRatio()));
+                } else {
+                    ProtocolDataContainer.getInstance().putGBoolean(rDataPointMap.get(i.getKey()).getId(), ((BooleanModbusDataInRegister) i.getValue()).getValue(0));
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 提取线圈数据
+     *
+     * @param cDataPointMap   数据点映射
+     * @param requestCoilList 请求线圈列表
+     * @throws ModbusException modbus例外
+     */
+    private void extractCoilData(Map<Integer, GatherDataPoint> cDataPointMap, List<Obj4RequestCoil> requestCoilList) throws ModbusException {
+        if (requestCoilList != null) {
+            Map<Integer, Boolean> map = ModbusRequestDataUtils.getCoilData((AbstractMasterBuilder) this.protocolBuilder, requestCoilList);
+            for (Map.Entry<Integer, Boolean> i : map.entrySet()) {
+                ProtocolDataContainer.getInstance().putGBoolean(cDataPointMap.get(i.getKey()).getId(), i.getValue());
+            }
+        }
+    }
+}

+ 130 - 0
src/main/java/tunnelworker/masters/iml/CdtRtuMaster.java

@@ -0,0 +1,130 @@
+package tunnelworker.masters.iml;
+
+
+import container.ProtocolTunnelContainer;
+import entity.datapoint.CommandDataPoint;
+import entity.datapoint.GatherDataPoint;
+import enums.TunnelStatus;
+import exception.DataExchangeException;
+import lombok.Getter;
+import mastertunnelinfo.MasterCdtRtuTunnelInfo;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.masters.MasterInterface;
+import tunnelworker.workassist.SingleThreadPoolExecutorUtil;
+import tunnelworker.workassist.cdt.MyCDTDataHandler;
+import wei.yigulu.cdt.netty.CDTMaster;
+import wei.yigulu.modbus.domain.datatype.ModbusDataTypeEnum;
+import wei.yigulu.purejavacomm.PureJavaCommChannelConfig;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * cdt 协议 Rtu获取
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@SuppressWarnings("ALL")
+public class CdtRtuMaster extends BaseProtocolTunnel<MasterCdtRtuTunnelInfo, CDTMaster> implements MasterInterface {
+
+
+    /**
+     * 遥测点表
+     */
+    @Getter
+    private Map<Integer, GatherDataPoint> cdtYcDataPoints;
+    /**
+     * 遥信点表
+     */
+    @Getter
+    private Map<Integer, GatherDataPoint> cdtYxDataPoints;
+
+
+    /**
+     * 构造方法
+     *
+     * @param gatherCdtRtuTunnelInfo 通道信息
+     */
+    public CdtRtuMaster(MasterCdtRtuTunnelInfo gatherCdtRtuTunnelInfo) {
+        super(gatherCdtRtuTunnelInfo);
+    }
+
+
+    @Override
+    protected CdtRtuMaster buildTunnel() throws DataExchangeException {
+        parseDataPoint();
+        this.protocolBuilder = new CDTMaster(getTunnelInfo().getSerialName(), new MyCDTDataHandler(this).setLog(log));
+        this.protocolBuilder.setLog(log);
+        this.protocolBuilder.setBaudRate(getTunnelInfo().getBaudRate()).setParity(PureJavaCommChannelConfig.Paritybit.valueOf(getTunnelInfo().getParity()));
+        this.setTunnelStatus(TunnelStatus.BUILT);
+        ProtocolTunnelContainer.getInstance().addTunnel(this);
+        log.info("成功创建CDT  rtu Master通道对象:{}", this.tunnelInfo.getTunnelName());
+        return this;
+    }
+
+    @Override
+    protected void parseDataPoint() throws DataExchangeException {
+        List<GatherDataPoint> dataPoints = this.getTunnelInfo().getDataPoints();
+        cdtYxDataPoints = new HashMap<>();
+        cdtYcDataPoints = new HashMap<>();
+        if (dataPoints == null || dataPoints.isEmpty()) {
+            return;
+        }
+        for (GatherDataPoint d : dataPoints) {
+            if (ModbusDataTypeEnum.A16.equals(d.getDataType().getModbusDataType())) {
+                cdtYxDataPoints.put(d.getProtocolPoint(), d);
+            } else {
+                cdtYcDataPoints.put(d.getProtocolPoint(), d);
+            }
+        }
+
+    }
+
+
+    @Override
+    public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
+        if (this.protocolBuilder != null) {
+            protocolBuilder.stop();
+        }
+        log.info("关闭 cdt Master 通道 {}", this.tunnelInfo.getTunnelName());
+        return super.tunnelStop();
+    }
+
+
+    @Override
+    public CdtRtuMaster startTunnel() throws DataExchangeException {
+        log.info("CDT RTU master {} 通道开始连接", getTunnelInfo().getTunnelName());
+        protocolTunnelContainer.addUpdateDateTask(this);
+        try {
+            SingleThreadPoolExecutorUtil.executeBySingleThreadExecutor(() -> {
+                this.protocolBuilder.create();
+                log.error("cdt rtu master  创建通道失败");
+                setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+            });
+        } catch (Exception e) {
+            log.error("cdt rtu master 创建通道失败", e);
+            setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+            throw new DataExchangeException(10007, "cdt rtu master创建通道失败");
+        }
+        if (!TunnelStatus.LISTENSERIALFAIL.equals(getTunnelStatus())) {
+            setTunnelStatus(TunnelStatus.LISTENSERIALSUCCESS);
+        }
+        return this;
+    }
+
+    @Override
+    public void getData() throws DataExchangeException {
+        return;
+    }
+
+    @Override
+    public boolean sendCommand(CommandDataPoint dataPoint, Object val) throws DataExchangeException {
+        return false;
+    }
+
+
+
+}

+ 179 - 0
src/main/java/tunnelworker/masters/iml/Iec104TcpMaster.java

@@ -0,0 +1,179 @@
+package tunnelworker.masters.iml;
+
+
+import ch.qos.logback.classic.Logger;
+
+import entity.datapoint.CommandDataPoint;
+import entity.datapoint.GatherDataPoint;
+import enums.CommandProtocolType;
+import enums.TunnelStatus;
+import exception.DataExchangeException;
+import mastertunnelinfo.Master104TcpTunnelInfo;
+import org.slf4j.LoggerFactory;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.masters.MasterInterface;
+import tunnelworker.workassist.SingleThreadPoolExecutorUtil;
+import wei.yigulu.iec104.nettyconfig.Iec104HSMasterBuilder;
+import wei.yigulu.iec104.util.SendCommandHelper;
+import wei.yigulu.iec104.util.SendDataFrameHelper;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 104协议数据获取
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+
+public class Iec104TcpMaster extends BaseProtocolTunnel<Master104TcpTunnelInfo, Iec104HSMasterBuilder> implements MasterInterface {
+
+    private static final Logger logger = (Logger) LoggerFactory.getLogger(Iec104TcpMaster.class);
+
+    /**
+     * 点表
+     */
+    private Map<Integer, GatherDataPoint> iec104DataPoint = new HashMap<>();
+
+
+    /**
+     * iec104tcp数据采集
+     * 构造方法
+     *
+     * @param gather104TcpTunnelInfo 104采集通道信息
+     * @throws DataExchangeException 数据交换异常
+     */
+    public Iec104TcpMaster(Master104TcpTunnelInfo gather104TcpTunnelInfo) throws DataExchangeException {
+        super(gather104TcpTunnelInfo);
+        parseDataPoint();
+    }
+
+    @Override
+    protected void parseDataPoint() throws DataExchangeException {
+        List<GatherDataPoint> dataPoints = this.getTunnelInfo().getDataPoints();
+        iec104DataPoint = new HashMap<>();
+        if (dataPoints == null || dataPoints.size() == 0) {
+            return;
+        }
+        for (GatherDataPoint d : dataPoints) {
+            this.iec104DataPoint.put(d.getProtocolPoint(), d);
+        }
+        if (protocolBuilder != null) {
+            protocolBuilder.getConfigInfoMap().put("104DataPoint", this.iec104DataPoint);
+        }
+    }
+
+
+    @Override
+    protected Iec104TcpMaster buildTunnel() throws DataExchangeException {
+        protocolBuilder = new MyMasterBuilder(tunnelInfo.getRemoteIp(), tunnelInfo.getRemotePort());
+        protocolBuilder.getConfigInfoMap().put("tunnelId", this.tunnelInfo.getId());
+        this.protocolBuilder.getConfigInfoMap().put("tunnelName", this.tunnelInfo.getTunnelName());
+        protocolBuilder.getConfigInfoMap().put("104DataPoint", this.iec104DataPoint);
+        protocolBuilder.setLog(log);
+        if (getTunnelInfo().getRemoteSpareIp() != null) {
+            protocolBuilder.setSpareIp(getTunnelInfo().getRemoteSpareIp());
+        }
+        if (getTunnelInfo().getRemoteSparePort() != null) {
+            protocolBuilder.setSparePort(getTunnelInfo().getRemoteSparePort());
+        }
+        if (getTunnelInfo().getSelfIp() != null) {
+            protocolBuilder.setSelfIp(getTunnelInfo().getSelfIp());
+        }
+        if (getTunnelInfo().getSelfPort() != null) {
+            protocolBuilder.setSelfPort(getTunnelInfo().getSelfPort());
+        }
+        getProtocolTunnelContainer().addTunnel(this);
+        this.setTunnelStatus(TunnelStatus.BUILT);
+        log.info("成功创建Iec 104Tcp master通道对象:{}", this.tunnelInfo.getTunnelName());
+        return this;
+    }
+
+    @Override
+    public Iec104TcpMaster startTunnel() throws DataExchangeException {
+        protocolTunnelContainer.addUpdateDateTask(this);
+        try {
+            SingleThreadPoolExecutorUtil.executeBySingleThreadExecutor(() -> this.protocolBuilder.create());
+        } catch (Exception e) {
+            throw new DataExchangeException(10009, "104master通道创建时发生异常");
+        }
+        log.info("IEC104 TCP master {} 通道开始连接", getTunnelInfo().getTunnelName());
+        return this;
+    }
+
+
+    @Override
+    public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
+        if (this.protocolBuilder != null) {
+            this.protocolBuilder.stop();
+        }
+        log.info("104 master {} 关闭", tunnelInfo.getTunnelName());
+        return super.tunnelStop();
+    }
+
+    @Override
+    public void getData() throws DataExchangeException {
+        log.info("向104 通道:{} 中发送总召唤", getTunnelInfo().getTunnelName());
+        try {
+            if (this.protocolBuilder.getFuture().channel() != null && this.protocolBuilder.getFuture().channel().isActive()) {
+                SendDataFrameHelper.sendTotalSummonFrame(this.protocolBuilder.getFuture().channel(), getTunnelInfo().getPublicAddress(), 6, this.log);
+            }
+        } catch (Exception e) {
+            log.error("104通道发送数据时发生异常", e);
+            throw new DataExchangeException(10008, "104通道发送数据时发生异常");
+        }
+    }
+
+    @Override
+    public boolean sendCommand(CommandDataPoint dataPoint, Object val) throws Exception {
+        logger.debug("通道{}发送控制命令,命令类型{},命令地址{},控制值{}", tunnelInfo.getTunnelName(), dataPoint.getCommandProtocolType().getExplain(), dataPoint.getProtocolPoint(), val);
+        logger.debug("当下通道状态:{}", getTunnelStatus());
+        if (dataPoint.getCommandProtocolType() == CommandProtocolType.SHORT_FLOAT) {
+            return SendCommandHelper.sendShortCommand(protocolBuilder, 0, tunnelInfo.getPublicAddress(), dataPoint.getProtocolPoint(), ((BigDecimal) val).multiply(dataPoint.getRatio()).floatValue());
+        } else {
+            return false;
+        }
+    }
+
+
+    /**
+     * 内部类 用于设置连接 和断连 的后续动作
+     */
+    class MyMasterBuilder extends Iec104HSMasterBuilder {
+        public MyMasterBuilder(String ip, Integer port) {
+            super(ip, port);
+        }
+
+
+        @Override
+        public void connected() {
+            log.info("IEC104 Master {}连接成功", getTunnelInfo().getTunnelName());
+            setTunnelStatus(TunnelStatus.CONNECTED);
+            try {
+                Executors.newSingleThreadScheduledExecutor().schedule(() -> {
+                    try {
+                        getData();
+                    } catch (DataExchangeException e) {
+                        log.error("通道连接时发送总召唤异常");
+                    }
+                }, 3L, TimeUnit.SECONDS);
+            } catch (Exception e) {
+                log.error("通道连接时发送总召唤异常");
+            }
+        }
+
+        @Override
+        public void disconnected() {
+            log.error("IEC104 Master {}断开连接", getTunnelInfo().getTunnelName());
+            setTunnelStatus(TunnelStatus.LOSECONN);
+        }
+
+
+    }
+
+}

+ 96 - 0
src/main/java/tunnelworker/masters/iml/ModbusRtuMaster.java

@@ -0,0 +1,96 @@
+package tunnelworker.masters.iml;
+
+
+import enums.TunnelStatus;
+import exception.DataExchangeException;
+import mastertunnelinfo.MasterModbusRtuTunnelInfo;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.workassist.SingleThreadPoolExecutorUtil;
+import wei.yigulu.modbus.netty.ModbusRtuMasterBuilder;
+import wei.yigulu.purejavacomm.PureJavaCommChannelConfig;
+
+/**
+ * modbus RTU 采集通道
+ *
+ * @author: xiuwei
+ * @version: 3.0
+ */
+public class ModbusRtuMaster extends BaseModbusMaster<MasterModbusRtuTunnelInfo, ModbusRtuMasterBuilder> {
+
+
+    /**
+     * 构造方法
+     *
+     * @param gatherModbusRtuTunnelInfo 通道信息
+     */
+    public ModbusRtuMaster(final MasterModbusRtuTunnelInfo gatherModbusRtuTunnelInfo) {
+        super(gatherModbusRtuTunnelInfo);
+    }
+
+    @Override
+    protected ModbusRtuMaster buildTunnel() throws DataExchangeException {
+        setSlaveId(tunnelInfo.getSlaveId());
+        protocolBuilder = new ModbusRtuMasterBuilder(this.tunnelInfo.getSerialName());
+        protocolBuilder.setBaudRate(tunnelInfo.getBaudRate());
+        protocolBuilder.setDataBits(PureJavaCommChannelConfig.Databits.valueOf(tunnelInfo.getDataBits()));
+        protocolBuilder.setStopBits(PureJavaCommChannelConfig.Stopbits.valueOf(tunnelInfo.getStopBits()));
+        protocolBuilder.setParity(PureJavaCommChannelConfig.Paritybit.valueOf(tunnelInfo.getParity()));
+        protocolBuilder.setReadTimeOut(500);
+        protocolBuilder.setLog(this.log);
+        setTunnelStatus(TunnelStatus.BUILT);
+        protocolTunnelContainer.addTunnel(this);
+        log.info("成功创建ModbusRTUMaster通道对象:{}", this.tunnelInfo.getTunnelName());
+        return this;
+    }
+
+
+    @Override
+    public ModbusRtuMaster startTunnel() throws DataExchangeException {
+        log.info("modbus RTU master {} 通道开始连接", getTunnelInfo().getTunnelName());
+        protocolTunnelContainer.addUpdateDateTask(this);
+        try {
+            SingleThreadPoolExecutorUtil.executeBySingleThreadExecutor(() -> {
+                try {
+                    this.protocolBuilder.create();
+                    log.error("Modbus Rtu Master  创建通道失败");
+                    setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+                } catch (Exception e) {
+                    log.error("Modbus Rtu Master  创建通道失败", e);
+                    setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+                }
+            });
+        } catch (Exception e) {
+            log.error("Modbus Rtu Master 创建通道失败", e);
+            setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+            throw new DataExchangeException(10007, "Modbus Rtu Master创建通道失败");
+        }
+        if (!TunnelStatus.LISTENSERIALFAIL.equals(getTunnelStatus())) {
+            setTunnelStatus(TunnelStatus.LISTENSERIALSUCCESS);
+        }
+        return this;
+    }
+
+    @Override
+    public void getData() throws DataExchangeException {
+        try {
+            super.getData();
+        } catch (DataExchangeException e) {
+            this.setTunnelStatus(TunnelStatus.LISTENPORTSUCCESSANDCONNANDCOMMERROR);
+            throw e;
+        }
+        this.setTunnelStatus(TunnelStatus.LISTENSERIALSUCCESS);
+    }
+
+    @Override
+    public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
+        if (this.protocolBuilder != null) {
+            this.protocolBuilder.stop();
+        }
+        log.info("关闭 Modbus Rtu Master 通道 {}", this.tunnelInfo.getTunnelName());
+        return super.tunnelStop();
+    }
+
+
+}
+
+

+ 108 - 0
src/main/java/tunnelworker/masters/iml/ModbusTcpMaster.java

@@ -0,0 +1,108 @@
+package tunnelworker.masters.iml;
+
+
+import enums.TunnelStatus;
+import exception.DataExchangeException;
+import mastertunnelinfo.MasterModbusTcpTunnelInfo;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.workassist.SingleThreadPoolExecutorUtil;
+import wei.yigulu.modbus.netty.HSModbusTcpMasterBuilder;
+
+/**
+ * modbus协议TCP数据采集
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+
+public class ModbusTcpMaster extends BaseModbusMaster<MasterModbusTcpTunnelInfo, HSModbusTcpMasterBuilder> {
+
+
+    /**
+     * 构造方法
+     *
+     * @param gatherModbusTcpTunnelInfo 通道信息
+     */
+    public ModbusTcpMaster(final MasterModbusTcpTunnelInfo gatherModbusTcpTunnelInfo) {
+        super(gatherModbusTcpTunnelInfo);
+    }
+
+    @Override
+    protected ModbusTcpMaster buildTunnel() throws DataExchangeException {
+        setSlaveId(tunnelInfo.getSlaveId());
+        protocolBuilder = new ModbusMaster(tunnelInfo.getRemoteIp(), tunnelInfo.getRemotePort());
+        protocolBuilder.setSpareIp(tunnelInfo.getRemoteSpareIp());
+        protocolBuilder.setSparePort(tunnelInfo.getRemoteSparePort());
+        protocolBuilder.setSelfIp(tunnelInfo.getSelfIp());
+        protocolBuilder.setSelfPort(tunnelInfo.getSelfPort());
+        protocolBuilder.setLog(this.log);
+        setTunnelStatus(TunnelStatus.BUILT);
+        protocolTunnelContainer.addTunnel(this);
+        log.info("成功创建ModbusTCPMaster通道对象:{}", this.tunnelInfo.getTunnelName());
+        return this;
+    }
+
+
+    @Override
+    public ModbusTcpMaster startTunnel() throws DataExchangeException {
+        log.info("modbus TCP master {} 通道开始连接", getTunnelInfo().getTunnelName());
+        protocolTunnelContainer.addUpdateDateTask(this);
+        try {
+            SingleThreadPoolExecutorUtil.executeBySingleThreadExecutor(() -> {
+                try {
+                    this.protocolBuilder.create();
+                    log.error("modbus TCP master  创建通道失败");
+                    setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
+                } catch (Exception e) {
+                    log.error("modbus TCP master  创建通道失败", e);
+                    setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
+                }
+            });
+        } catch (Exception e) {
+            log.error("modbus TCP master 创建通道失败", e);
+            setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
+            throw new DataExchangeException(10007, "modbus TCP master创建通道失败");
+        }
+        if (!TunnelStatus.LISTENPORTFAIL.equals(getTunnelStatus())) {
+            setTunnelStatus(TunnelStatus.LISTENPORTSUCCESS);
+        }
+        return this;
+    }
+
+
+    @Override
+    public void getData() throws DataExchangeException {
+        try {
+            super.getData();
+        } catch (DataExchangeException e) {
+            this.setTunnelStatus(TunnelStatus.CONNECTEDANDCOMMERROR);
+            throw e;
+        }
+        this.setTunnelStatus(TunnelStatus.CONNECTED);
+    }
+
+
+    @Override
+    public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
+        if (this.protocolBuilder != null) {
+            this.protocolBuilder.stop();
+        }
+        log.info("关闭 modbus TCP master 通道 {}", this.tunnelInfo.getTunnelName());
+        return super.tunnelStop();
+    }
+
+
+    class ModbusMaster extends HSModbusTcpMasterBuilder {
+
+        public ModbusMaster(String ip, Integer port) {
+            super(ip, port);
+        }
+
+        @Override
+        public void connected() {
+            setTunnelStatus(TunnelStatus.CONNECTED);
+        }
+
+    }
+
+}

+ 11 - 0
src/main/java/tunnelworker/package-info.java

@@ -0,0 +1,11 @@
+/**
+ * 通道的工作者
+ * 包括发送者和接受者
+ * 以及发送者和接收者的接口
+ * workassist 是通道创建使用过程中所需的辅助类
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+package tunnelworker;
+

+ 23 - 0
src/main/java/tunnelworker/slavers/SlaverInterface.java

@@ -0,0 +1,23 @@
+package tunnelworker.slavers;
+
+
+import exception.DataExchangeException;
+
+/**
+ * 协议转发数据通道
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+
+public interface SlaverInterface {
+
+	/**
+	 * 更新协议要发送的数据内容
+	 *
+	 * @throws DataExchangeException data exchange exception
+	 */
+	void updateData2Protocol() throws DataExchangeException;
+
+
+}

+ 119 - 0
src/main/java/tunnelworker/slavers/iml/CdtRtuSlaver.java

@@ -0,0 +1,119 @@
+package tunnelworker.slavers.iml;
+
+
+
+import container.ProtocolDataContainer;
+import entity.datapoint.CommandDataPoint;
+import entity.datapoint.SenderDataPoint;
+import enums.TunnelStatus;
+import exception.DataExchangeException;
+import slavertunnelinfo.SlaverCdtRtuTunnelInfo;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.slavers.SlaverInterface;
+import tunnelworker.workassist.SingleThreadPoolExecutorUtil;
+import tunnelworker.workassist.cdt.MyCDTDataTransmitter;
+import wei.yigulu.cdt.netty.CDTSlaver;
+import wei.yigulu.modbus.domain.datatype.ModbusDataTypeEnum;
+import wei.yigulu.purejavacomm.PureJavaCommChannelConfig;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * cdt Rtu 采集通道
+ * 转发点表 默认点表连续
+ *
+ * @author: xiuwei
+ * @version:
+ */
+public class CdtRtuSlaver extends BaseProtocolTunnel<SlaverCdtRtuTunnelInfo, CDTSlaver> implements SlaverInterface {
+
+
+    /**
+     * 自定义的CDT slave 的数据发送逻辑   CDTDataTransmitter的实现类
+     */
+    MyCDTDataTransmitter myCDTDataTransmitter = new MyCDTDataTransmitter(this);
+
+
+    /**
+     * 顶层的构造方法
+     *
+     * @param tunnelInfo cdt转发的通道信息
+     */
+    public CdtRtuSlaver(SlaverCdtRtuTunnelInfo tunnelInfo) throws DataExchangeException {
+        super(tunnelInfo);
+        parseDataPoint();
+    }
+
+    @Override
+    public BaseProtocolTunnel startTunnel() throws DataExchangeException {
+        log.info("Cdt Rtu slaver {}  开始监听", getTunnelInfo().getTunnelName());
+        protocolTunnelContainer.addUpdateDateTask(this);
+        if (this.protocolBuilder != null && !TunnelStatus.BUILT.equals(this.getTunnelStatus())) {
+            protocolBuilder.stop();
+            buildTunnel();
+        }
+        try {
+            SingleThreadPoolExecutorUtil.executeBySingleThreadExecutor(() -> {
+                protocolBuilder.create();
+                log.warn("cdt slaver {} 串口监听失败", getTunnelInfo().getTunnelName());
+                this.setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+            });
+        } catch (Exception e) {
+            log.warn("cdt slaver {} 串口监听异常", getTunnelInfo().getTunnelName());
+            this.setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+            throw new DataExchangeException(10013, "cdt rtu slave通道创建时发生异常");
+        }
+        if (!TunnelStatus.LISTENSERIALFAIL.equals(getTunnelStatus())) {
+            setTunnelStatus(TunnelStatus.LISTENSERIALSUCCESS);
+        }
+        return this;
+    }
+
+    @Override
+    protected BaseProtocolTunnel buildTunnel() throws DataExchangeException {
+        parseDataPoint();
+        protocolBuilder = new CDTSlaver(getTunnelInfo().getSerialName(), myCDTDataTransmitter.setLog(log));
+        protocolBuilder.setLog(log);
+        protocolBuilder.setBaudRate(getTunnelInfo().getBaudRate()).setDataBits(PureJavaCommChannelConfig.Databits.valueOf(8)).setParity(PureJavaCommChannelConfig.Paritybit.valueOf(this.getTunnelInfo().getParity()));
+        this.setTunnelStatus(TunnelStatus.BUILT);
+        getProtocolTunnelContainer().addTunnel(this);
+        log.info("成功创建CdtRtuSlaver通道对象:{}", this.tunnelInfo.getTunnelName());
+        return this;
+    }
+
+    @Override
+    public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
+        if (this.protocolBuilder != null) {
+            this.protocolBuilder.stop();
+        }
+        log.info("关闭CdtRtuSlaver通道:{}", this.tunnelInfo.getTunnelName());
+        return super.tunnelStop();
+    }
+
+
+
+    @Override
+    protected void parseDataPoint() throws DataExchangeException {
+        return;
+    }
+
+
+    @Override
+    public void updateData2Protocol() {
+        ProtocolDataContainer protocolDataContainerRedis = ProtocolDataContainer.getInstance();
+        Map<Integer, Boolean> yxVal = new HashMap<>();
+        Map<Integer, Integer> ycVal = new HashMap<>();
+        for (SenderDataPoint d : this.getTunnelInfo().getDataPoints()) {
+            if (ModbusDataTypeEnum.A16.equals(d.getDataType())) {
+                yxVal.put(d.getProtocolPoint(), protocolDataContainerRedis.getBoolean(d.getDataPointId()));
+            } else {
+                ycVal.put(d.getProtocolPoint(), protocolDataContainerRedis.getNumber(d.getDataPointId()).multiply(d.getRatio()).intValue());
+            }
+        }
+        myCDTDataTransmitter.setImportantYc(ycVal);
+        myCDTDataTransmitter.setYx(yxVal);
+    }
+
+
+}

+ 161 - 0
src/main/java/tunnelworker/slavers/iml/Iec104TcpSlaver.java

@@ -0,0 +1,161 @@
+package tunnelworker.slavers.iml;
+
+
+
+import container.ProtocolDataContainer;
+import entity.datagetter.BaseDataGetter;
+import entity.datagetter.ContainerBoolDataGetter;
+import entity.datagetter.ContainerNumDataGetter;
+import entity.datapoint.SenderDataPoint;
+import enums.TunnelStatus;
+import exception.DataExchangeException;
+import io.netty.channel.Channel;
+import slavertunnelinfo.Slaver104TcpTunnelInfo;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.slavers.SlaverInterface;
+import tunnelworker.workassist.SingleThreadPoolExecutorUtil;
+import wei.yigulu.iec104.nettyconfig.Iec104SlaverBuilder;
+import wei.yigulu.iec104.util.SendDataFrameHelper;
+import wei.yigulu.modbus.domain.datatype.ModbusDataTypeEnum;
+
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 104转发的通道实体
+ * <p>
+ * 转发点表默认连续  遥信遥测分区
+ *
+ * @author: xiuwei
+ * @version:
+ */
+public class Iec104TcpSlaver extends BaseProtocolTunnel<Slaver104TcpTunnelInfo, Iec104SlaverBuilder> implements SlaverInterface {
+
+
+    /**
+     * 遥测点位的对应关系
+     */
+    Map<Integer, BaseDataGetter<Number>> ycDataPoints = new HashMap<>();
+
+
+    /**
+     * 遥信点位的对应关系
+     */
+    Map<Integer, BaseDataGetter<Boolean>> yxDataPoints = new HashMap<>();
+
+
+    /**
+     * 顶层的构造方法
+     *
+     * @param tunnelInfo 通道信息
+     */
+    public Iec104TcpSlaver(Slaver104TcpTunnelInfo tunnelInfo) throws DataExchangeException {
+        super(tunnelInfo);
+        parseDataPoint();
+    }
+
+    @Override
+    public Iec104TcpSlaver startTunnel() throws DataExchangeException {
+        protocolTunnelContainer.addUpdateDateTask(this);
+        try {
+            SingleThreadPoolExecutorUtil.submitBySingleThreadExecutor(() -> {
+                try {
+                    this.protocolBuilder.create();
+                } catch (Exception e) {
+                    log.error("创建104Sender失败", e);
+                    this.setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
+                }
+            });
+        } catch (Exception e) {
+            setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
+            log.error("104slaver通道创建时发生异常", e);
+            throw new DataExchangeException(10010, "104slaver通道创建时发生异常");
+        }
+        if (!TunnelStatus.LISTENPORTFAIL.equals(getTunnelStatus())) {
+            setTunnelStatus(TunnelStatus.LISTENPORTSUCCESS);
+        }
+        return this;
+    }
+
+    @Override
+    protected Iec104TcpSlaver buildTunnel() throws DataExchangeException {
+        this.protocolBuilder = new My104Slaver(this.tunnelInfo.getSelfPort());
+        this.protocolBuilder.setIp(this.tunnelInfo.getSelfIp());
+        this.protocolBuilder.getConfigInfoMap().put("tunnelId", this.tunnelInfo.getId());
+        this.protocolBuilder.getConfigInfoMap().put("tunnelName", this.tunnelInfo.getTunnelName());
+        this.protocolBuilder.setLog(log);
+        this.protocolBuilder.getConfigInfoMap().put("yx", this.yxDataPoints);
+        this.protocolBuilder.getConfigInfoMap().put("yc", this.ycDataPoints);
+        this.setTunnelStatus(TunnelStatus.BUILT);
+        protocolTunnelContainer.addTunnel(this);
+        log.info("成功创建Iec 104Tcp slaver通道对象:{}", this.tunnelInfo.getTunnelName());
+        return this;
+    }
+
+    @Override
+    protected void parseDataPoint() throws DataExchangeException {
+        log.info("解析通道下所需点位");
+        List<SenderDataPoint> dataPoints = this.getTunnelInfo().getDataPoints();
+        if (dataPoints == null || dataPoints.size() == 0) {
+            return;
+        }
+        yxDataPoints = new HashMap<>();
+        ycDataPoints = new HashMap<>();
+        for (SenderDataPoint d : dataPoints) {
+            if (ModbusDataTypeEnum.A16.equals(d.getDataType().getModbusDataType())) {
+                yxDataPoints.put(d.getProtocolPoint(),new ContainerBoolDataGetter(d));
+            } else {
+                ycDataPoints.put(d.getProtocolPoint(), new ContainerNumDataGetter(d));
+            }
+        }
+    }
+
+    @Override
+    public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
+        if (this.protocolBuilder != null) {
+            this.protocolBuilder.stop();
+        }
+        log.info("关闭IEC104通道:{}", this.tunnelInfo.getTunnelName());
+        return super.tunnelStop();
+    }
+
+    @Override
+    public void updateData2Protocol() throws DataExchangeException {
+        if (this.protocolBuilder.getChannels().size() == 0) {
+            log.debug("当前没有master联入");
+            setTunnelStatus(TunnelStatus.LISTENPORTSUCCESSANDNOCONN);
+            return;
+        }
+        log.info("向该通道的所有连接发送数据");
+        ProtocolDataContainer dataContainer = ProtocolDataContainer.getInstance();
+        Map<Integer, Number> yc = new HashMap<>();
+        this.ycDataPoints.forEach((k, v) -> yc.put(k, v.getValue()));
+        Map<Integer, Boolean> yx = new HashMap<>();
+        this.yxDataPoints.forEach((k, v) -> yx.put(k, v.getValue()));
+        for (Channel channel : this.protocolBuilder.getChannels()) {
+            try {
+                SendDataFrameHelper.sendYxDataFrameDiscontinuity(channel, yx, getTunnelInfo().getPublicAddress(), 3, log);
+                SendDataFrameHelper.sendYcDataFrameDiscontinuity(channel, yc, getTunnelInfo().getPublicAddress(), 3, log);
+            } catch (Exception e) {
+                log.error("104通道发送数据时发生异常", e);
+                e.printStackTrace();
+                throw new DataExchangeException(10008, "104通道发送数据时发生异常");
+            }
+        }
+    }
+
+    class My104Slaver extends Iec104SlaverBuilder {
+
+        public My104Slaver(int port) {
+            super(port);
+        }
+
+        public void connected(InetSocketAddress ipSocket) {
+            setTunnelStatus(TunnelStatus.LISTENPORTSUCCESSANDCONN);
+        }
+
+    }
+
+}

+ 139 - 0
src/main/java/tunnelworker/slavers/iml/ModbusRtuSlaver.java

@@ -0,0 +1,139 @@
+package tunnelworker.slavers.iml;
+
+
+import container.ProtocolDataContainer;
+import entity.datapoint.SenderDataPoint;
+import enums.TunnelStatus;
+import exception.DataExchangeException;
+import slavertunnelinfo.SlaverModbusRtuTunnelInfo;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.slavers.SlaverInterface;
+import tunnelworker.workassist.SingleThreadPoolExecutorUtil;
+import wei.yigulu.modbus.domain.datatype.BooleanModbusDataInRegister;
+import wei.yigulu.modbus.domain.datatype.ModbusDataTypeEnum;
+import wei.yigulu.modbus.domain.datatype.NumericModbusData;
+import wei.yigulu.modbus.netty.ModbusRtuSlaverBuilder;
+import wei.yigulu.purejavacomm.PureJavaCommChannelConfig;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * modbus Rtu 的数据转发通道
+ *
+ * @author: xiuwei
+ * @version:
+ */
+
+public class ModbusRtuSlaver extends BaseProtocolTunnel<SlaverModbusRtuTunnelInfo, ModbusRtuSlaverBuilder> implements SlaverInterface {
+
+
+    /**
+     * 通道点位  ---- 点位对象
+     */
+    Map<Integer, SenderDataPoint> dataPointMap;
+
+
+    /**
+     * 0: 线圈   1:寄存器
+     */
+    private int functionFlag;
+
+
+    /**
+     * 顶层的构造方法
+     *
+     * @param tunnelInfo 通道信息
+     */
+    public ModbusRtuSlaver(SlaverModbusRtuTunnelInfo tunnelInfo) {
+        super(tunnelInfo);
+    }
+
+    @Override
+    protected ModbusRtuSlaver buildTunnel() throws DataExchangeException {
+        protocolBuilder = new ModbusRtuSlaverBuilder(this.tunnelInfo.getSerialName());
+        protocolBuilder.setBaudRate(tunnelInfo.getBaudRate());
+        protocolBuilder.setDataBits(PureJavaCommChannelConfig.Databits.valueOf(tunnelInfo.getDataBits()));
+        protocolBuilder.setStopBits(PureJavaCommChannelConfig.Stopbits.valueOf(tunnelInfo.getStopBits()));
+        protocolBuilder.setParity(PureJavaCommChannelConfig.Paritybit.valueOf(tunnelInfo.getParity()));
+        protocolBuilder.setLog(this.log);
+        log.info("成功创建ModbusRTUSlaver通道对象:{}", this.tunnelInfo.getTunnelName());
+        setTunnelStatus(TunnelStatus.BUILT);
+        protocolTunnelContainer.addTunnel(this);
+        return this;
+    }
+
+    @Override
+    protected void parseDataPoint() throws DataExchangeException {
+        log.info("解析该通道下所管理的点位");
+        List<SenderDataPoint> dataPoints = this.tunnelInfo.getDataPoints();
+        if (dataPoints == null || dataPoints.isEmpty()) {
+            return;
+        }
+        this.dataPointMap = new HashMap<>();
+        for (SenderDataPoint d : dataPoints) {
+            this.dataPointMap.put(d.getProtocolPoint(), d);
+        }
+        log.info("解析该通道下所管理的点位完成");
+    }
+
+
+    @Override
+    public ModbusRtuSlaver startTunnel() throws DataExchangeException {
+        log.info("modbus RTU slaver {} 通道开始连接", getTunnelInfo().getTunnelName());
+        protocolTunnelContainer.addUpdateDateTask(this);
+        try {
+            SingleThreadPoolExecutorUtil.executeBySingleThreadExecutor(() -> {
+                try {
+                    this.protocolBuilder.create();
+                    log.error("Modbus Rtu slaver  创建通道失败");
+                    setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+                } catch (Exception e) {
+                    log.error("Modbus Rtu slaver  创建通道失败", e);
+                    setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+                }
+            });
+        } catch (Exception e) {
+            log.error("Modbus Rtu slaver 创建通道失败", e);
+            setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+            throw new DataExchangeException(10007, "Modbus Rtu slaver创建通道失败");
+        }
+        if (!TunnelStatus.LISTENSERIALFAIL.equals(getTunnelStatus())) {
+            setTunnelStatus(TunnelStatus.LISTENSERIALSUCCESS);
+        }
+        return this;
+    }
+
+
+    @Override
+    public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
+        if (this.protocolBuilder != null) {
+            this.protocolBuilder.stop();
+        }
+        log.info("关闭 Modbus Rtu slaver 通道 {}", this.tunnelInfo.getTunnelName());
+        return super.tunnelStop();
+    }
+
+
+    @Override
+    public void updateData2Protocol() throws DataExchangeException {
+        ProtocolDataContainer dataContainer = ProtocolDataContainer.getInstance();
+        if (functionFlag == 1) {
+            this.dataPointMap.forEach((k, v) -> {
+                if (ModbusDataTypeEnum.A16.equals(v.getDataType().getModbusDataType())) {
+                    BooleanModbusDataInRegister booleanModbusDataInRegister = new BooleanModbusDataInRegister();
+                    booleanModbusDataInRegister.setValue(0, dataContainer.getBoolean(v.getDataPointId()));
+                    this.protocolBuilder.getModbusSlaveDataContainer().setRegister(this.tunnelInfo.getSlaveId(), k, booleanModbusDataInRegister);
+                } else {
+                    this.protocolBuilder.getModbusSlaveDataContainer().setRegister(this.tunnelInfo.getSlaveId(), k, ((NumericModbusData) (v.getDataType().getModbusDataType().getObject())).setValue(dataContainer.getNumber(v.getDataPointId()).multiply(v.getRatio())));
+                }
+            });
+        } else {
+            this.dataPointMap.forEach((k, v) ->
+                    this.protocolBuilder.getModbusSlaveDataContainer().setCoil(this.tunnelInfo.getSlaveId(), k, dataContainer.getBoolean(v.getDataPointId()))
+            );
+        }
+    }
+}

+ 129 - 0
src/main/java/tunnelworker/slavers/iml/ModbusTcpSlaver.java

@@ -0,0 +1,129 @@
+package tunnelworker.slavers.iml;
+
+
+import container.ProtocolDataContainer;
+import entity.datapoint.SenderDataPoint;
+import enums.TunnelStatus;
+import exception.DataExchangeException;
+import slavertunnelinfo.SlaverModbusTcpTunnelInfo;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.slavers.SlaverInterface;
+import tunnelworker.workassist.SingleThreadPoolExecutorUtil;
+import wei.yigulu.modbus.domain.datatype.BooleanModbusDataInRegister;
+import wei.yigulu.modbus.domain.datatype.ModbusDataTypeEnum;
+import wei.yigulu.modbus.domain.datatype.NumericModbusData;
+import wei.yigulu.modbus.netty.ModbusTcpSlaverBuilder;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * modbus 转发数据通道
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+public class ModbusTcpSlaver extends BaseProtocolTunnel<SlaverModbusTcpTunnelInfo, ModbusTcpSlaverBuilder> implements SlaverInterface {
+
+
+    /**
+     * 通道点位  ---- 点位对象
+     */
+    Map<Integer, SenderDataPoint> dataPointMap;
+
+
+    /**
+     * 0: 线圈   1:寄存器
+     */
+    private int functionFlag;
+
+
+    /**
+     * 顶层的构造方法
+     *
+     * @param tunnelInfo 通道信息
+     */
+    public ModbusTcpSlaver(SlaverModbusTcpTunnelInfo tunnelInfo) {
+        super(tunnelInfo);
+    }
+
+    @Override
+    protected ModbusTcpSlaver buildTunnel() throws DataExchangeException {
+        return this;
+    }
+
+    @Override
+    protected void parseDataPoint() throws DataExchangeException {
+        this.log.info("解析该通道下所管理的点位");
+        List<SenderDataPoint> dataPoints = this.tunnelInfo.getDataPoints();
+        if (dataPoints == null || dataPoints.size() == 0) {
+            return;
+        }
+        this.dataPointMap = new HashMap<>();
+        for (SenderDataPoint d : dataPoints) {
+            this.dataPointMap.put(d.getProtocolPoint(), d);
+        }
+        this.log.info("解析该通道下所管理的点位完成");
+    }
+
+
+    @Override
+    public ModbusTcpSlaver startTunnel() throws DataExchangeException {
+        log.info("modbus TCP slaver {} 通道开始连接", getTunnelInfo().getTunnelName());
+        protocolTunnelContainer.addUpdateDateTask(this);
+        try {
+            SingleThreadPoolExecutorUtil.executeBySingleThreadExecutor(() -> {
+                try {
+                    this.protocolBuilder.create();
+                    log.error("Modbus TCP slaver  创建通道失败");
+                    setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
+                } catch (Exception e) {
+                    log.error("Modbus TCP slaver  创建通道失败", e);
+                    setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
+                }
+            });
+        } catch (Exception e) {
+            log.error("Modbus TCP slaver 创建通道失败", e);
+            setTunnelStatus(TunnelStatus.LISTENPORTFAIL);
+            throw new DataExchangeException(10007, "Modbus TCP slaver创建通道失败");
+        }
+        if (!TunnelStatus.LISTENPORTFAIL.equals(getTunnelStatus())) {
+            setTunnelStatus(TunnelStatus.LISTENPORTSUCCESS);
+        }
+        return this;
+    }
+
+
+    @Override
+    public BaseProtocolTunnel tunnelStop() throws DataExchangeException {
+        if (this.protocolBuilder != null) {
+            this.protocolBuilder.stop();
+        }
+        log.info("关闭 Modbus TCP slaver 通道 {}", this.tunnelInfo.getTunnelName());
+        return super.tunnelStop();
+    }
+
+
+    @Override
+    public void updateData2Protocol() throws DataExchangeException {
+        ProtocolDataContainer dataContainer = ProtocolDataContainer.getInstance();
+        if (functionFlag == 1) {
+            this.dataPointMap.forEach((k, v) -> {
+                if (ModbusDataTypeEnum.A16.equals(v.getDataType().getModbusDataType())) {
+                    BooleanModbusDataInRegister booleanModbusDataInRegister = new BooleanModbusDataInRegister();
+                    booleanModbusDataInRegister.setValue(0, dataContainer.getBoolean(v.getDataPointId()));
+                    this.protocolBuilder.getModbusSlaveDataContainer().setRegister(this.tunnelInfo.getSlaveId(), k, booleanModbusDataInRegister);
+                } else {
+                    this.protocolBuilder.getModbusSlaveDataContainer().setRegister(this.tunnelInfo.getSlaveId(), k, ((NumericModbusData) (v.getDataType().getModbusDataType().getObject())).setValue(dataContainer.getNumber(v.getDataPointId()).multiply(v.getRatio())));
+                }
+            });
+        } else {
+            this.dataPointMap.forEach((k, v) ->
+                    this.protocolBuilder.getModbusSlaveDataContainer().setCoil(this.tunnelInfo.getSlaveId(), k, dataContainer.getBoolean(v.getDataPointId()))
+            );
+        }
+    }
+
+}

+ 77 - 0
src/main/java/tunnelworker/workassist/SingleThreadPoolExecutorUtil.java

@@ -0,0 +1,77 @@
+package tunnelworker.workassist;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.*;
+
+/**
+ * 用以获取单个线程的线程池以执行单个的线程任务
+ *
+ * @author: xiuwei
+ * @version:
+ */
+public class SingleThreadPoolExecutorUtil {
+
+	/**
+	 * 线程工厂
+	 */
+	private static final ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("SingleThreadExecutor-1").build();
+
+
+	/**
+	 * 获取执行线程  只允许有一个线程 提交的其他任务也会被拒绝
+	 * 线程池可自行销毁
+	 *
+	 * @return 执行线程池 ExecutorService
+	 */
+	public static ExecutorService getSingleThreadExecutor() {
+		return new ThreadPoolExecutor(0, 1,
+				30L, TimeUnit.MILLISECONDS,
+				new LinkedBlockingQueue<>(1), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
+	}
+
+
+	/**
+	 * 以单线程池的方式提交线程任务
+	 *
+	 * @param callable 线程任务
+	 * @return 执行的任务
+	 */
+	public static <T> Future<T> submitBySingleThreadExecutor(Callable<T> callable) {
+		return getSingleThreadExecutor().submit(callable);
+
+	}
+
+
+	/**
+	 * 以单线程池的方式执行线程任务
+	 *
+	 * @param runnable 线程任务
+	 */
+	public static void executeBySingleThreadExecutor(Runnable runnable) {
+		getSingleThreadExecutor().execute(runnable);
+	}
+
+
+	/**
+	 * 以单线程池的方式提交线程任务
+	 *
+	 * @param runnable 线程任务
+	 * @return 执行的任务
+	 */
+	public static Future submitBySingleThreadExecutor(Runnable runnable) {
+		return getSingleThreadExecutor().submit(runnable);
+	}
+
+	/**
+	 * 以单独的线程执行该任务  异常可以外抛
+	 *
+	 * @param callable 线程执行任务
+	 * @return {@link FutureTask}
+	 */
+	public static FutureTask executeByThread(Callable callable) {
+		FutureTask future = new FutureTask(callable);
+		new Thread(future).start();
+		return future;
+	}
+}

+ 69 - 0
src/main/java/tunnelworker/workassist/TunnelBuilder.java

@@ -0,0 +1,69 @@
+package tunnelworker.workassist;
+
+
+import entity.BaseTunnelInfo;
+import exception.DataExchangeException;
+import mastertunnelinfo.Master104TcpTunnelInfo;
+import mastertunnelinfo.MasterCdtRtuTunnelInfo;
+import mastertunnelinfo.MasterModbusRtuTunnelInfo;
+import mastertunnelinfo.MasterModbusTcpTunnelInfo;
+import slavertunnelinfo.Slaver104TcpTunnelInfo;
+import slavertunnelinfo.SlaverCdtRtuTunnelInfo;
+import slavertunnelinfo.SlaverModbusRtuTunnelInfo;
+import slavertunnelinfo.SlaverModbusTcpTunnelInfo;
+import tunnelworker.BaseProtocolTunnel;
+import tunnelworker.masters.iml.CdtRtuMaster;
+import tunnelworker.masters.iml.Iec104TcpMaster;
+import tunnelworker.masters.iml.ModbusRtuMaster;
+import tunnelworker.masters.iml.ModbusTcpMaster;
+import tunnelworker.slavers.iml.CdtRtuSlaver;
+import tunnelworker.slavers.iml.Iec104TcpSlaver;
+import tunnelworker.slavers.iml.ModbusRtuSlaver;
+import tunnelworker.slavers.iml.ModbusTcpSlaver;
+
+/**
+ * 用Tunnel Info 创建通达
+ *
+ * @author: xiuwei
+ * @version:
+ */
+public class TunnelBuilder {
+
+	private TunnelBuilder() {
+		throw new IllegalStateException("Utility class");
+	}
+
+	/**
+	 * 建立通道    主要是加载通道的配置 信息  以及点位信息
+	 *
+	 * @param tunnelInfo 隧道信息
+	 * @return {@link BaseProtocolTunnel}
+	 * @throws DataExchangeException 数据交换异常
+	 */
+	public static BaseProtocolTunnel buildTunnel(BaseTunnelInfo tunnelInfo) throws DataExchangeException {
+		switch (tunnelInfo.getTunnelType()) {
+			case IEC104TCPMASTER:
+				return new Iec104TcpMaster((Master104TcpTunnelInfo) tunnelInfo).build();
+			case IEC104TCPSLAVE:
+				return new Iec104TcpSlaver((Slaver104TcpTunnelInfo) tunnelInfo).build();
+			case MODBUSRTUMASTER:
+				return new ModbusRtuMaster((MasterModbusRtuTunnelInfo) tunnelInfo).build();
+			case MODBUSTCPMASTER:
+				return new ModbusTcpMaster((MasterModbusTcpTunnelInfo) tunnelInfo).build();
+			case MODBUSRTUSLAVE:
+				return new ModbusRtuSlaver((SlaverModbusRtuTunnelInfo) tunnelInfo).build();
+			case MODBUSTCPSLAVE:
+				return new ModbusTcpSlaver((SlaverModbusTcpTunnelInfo) tunnelInfo).build();
+			case CDTRTUSLAVE:
+				return new CdtRtuSlaver((SlaverCdtRtuTunnelInfo) tunnelInfo).build();
+			case CDTRTUMASTER:
+				return new CdtRtuMaster((MasterCdtRtuTunnelInfo) tunnelInfo).build();
+			default:
+				return null;
+		}
+	}
+
+
+}
+
+

+ 93 - 0
src/main/java/tunnelworker/workassist/cdt/MyCDTDataHandler.java

@@ -0,0 +1,93 @@
+package tunnelworker.workassist.cdt;
+
+
+import container.ProtocolDataContainer;
+import enums.TunnelStatus;
+import lombok.NonNull;
+import tunnelworker.masters.iml.CdtRtuMaster;
+import wei.yigulu.cdt.cdtframe.AbstractCDTDataHandler;
+import wei.yigulu.cdt.cdtframe.BaseDateType;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 基于数据模块的cdt的数据处理类
+ *
+ * @author: xiuwei
+ * @version:
+ */
+public class MyCDTDataHandler extends AbstractCDTDataHandler {
+
+    ProtocolDataContainer protocolDataContainerRedis = ProtocolDataContainer.getInstance();
+
+
+    /**
+     * cdt rtu 的数据采集对象 用于采集数据
+     */
+    private CdtRtuMaster cdtRtuDataGather;
+
+    /**
+     * 构造方法
+     *
+     * @param cdtRtuDataGather cdt 采集通道实体
+     */
+    public MyCDTDataHandler(@NonNull CdtRtuMaster cdtRtuDataGather) {
+        this.cdtRtuDataGather = cdtRtuDataGather;
+    }
+
+
+    @Override
+    protected void processYx(List<BaseDateType> dates) {
+        for (BaseDateType dateType : dates) {
+            dateType.getDates().forEach((k, v) -> {
+                if (this.cdtRtuDataGather.getCdtYxDataPoints().containsKey(k)) {
+                    protocolDataContainerRedis.putGBoolean(this.cdtRtuDataGather.getCdtYxDataPoints().get(k).getId(), (Boolean) v);
+                }
+            });
+        }
+    }
+
+    /**
+     * 不分重要 次要和普通 统一处理遥测
+     *
+     * @param dates
+     */
+    private void processYc(List<BaseDateType> dates) {
+        for (BaseDateType dateType : dates) {
+            getLog().info("接收到数据:{}", dateType.getDates());
+            dateType.getDates().forEach((k, v) -> {
+                if (this.cdtRtuDataGather.getCdtYcDataPoints().containsKey(k)) {
+                    protocolDataContainerRedis.putGNumber(this.cdtRtuDataGather.getCdtYcDataPoints().get(k).getId(), BigDecimal.valueOf((Integer) v).multiply(this.cdtRtuDataGather.getCdtYcDataPoints().get(k).getRatio()));
+                }
+            });
+        }
+    }
+
+
+    @Override
+    protected void processImportantYc(List<BaseDateType> list) {
+        processYc(list);
+    }
+
+    @Override
+    protected void processSecondYc(List<BaseDateType> list) {
+        processYc(list);
+    }
+
+    @Override
+    protected void processCommonYc(List<BaseDateType> list) {
+        processYc(list);
+    }
+
+    @Override
+    public void connected() {
+        this.cdtRtuDataGather.setTunnelStatus(TunnelStatus.LISTENSERIALSUCCESS);
+    }
+
+
+    @Override
+    public void disconnected() {
+        this.cdtRtuDataGather.setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+    }
+}

+ 161 - 0
src/main/java/tunnelworker/workassist/cdt/MyCDTDataTransmitter.java

@@ -0,0 +1,161 @@
+package tunnelworker.workassist.cdt;
+
+import com.alibaba.fastjson.JSON;
+
+import enums.TunnelStatus;
+import lombok.Data;
+import tunnelworker.slavers.iml.CdtRtuSlaver;
+import wei.yigulu.cdt.cdtframe.*;
+
+import java.util.*;
+
+/**
+ * cdt 数据发送器的实现类
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Data
+public class MyCDTDataTransmitter extends AbstractCDTDataTransmitter {
+
+    /**
+     * 重要遥测  数据
+     */
+    Map<Integer, Integer> importantYc = new HashMap<>();
+    /**
+     * 次要遥测  数据
+     */
+    Map<Integer, Integer> secondYc = new HashMap<>();
+    /**
+     * 一般遥测  数据
+     */
+    Map<Integer, Integer> commonYc = new HashMap<>();
+    /**
+     * 遥信数据
+     */
+    Map<Integer, Boolean> yx = new HashMap<>();
+    private CdtRtuSlaver cdtRtuDataSender;
+
+
+    /**
+     * 构造方法
+     *
+     * @param cdtRtuDataSender cdt发送实体
+     */
+    public MyCDTDataTransmitter(CdtRtuSlaver cdtRtuDataSender) {
+        this.cdtRtuDataSender = cdtRtuDataSender;
+    }
+
+    @Override
+    public List<CDTFrameBean> transmitImportantYc() {
+        return buildYc(CDTType.IMPORTANTYC, importantYc);
+    }
+
+    @Override
+    public List<CDTFrameBean> transmitSecondYc() {
+        return new ArrayList<>();
+    }
+
+    @Override
+    public List<CDTFrameBean> transmitCommonYc() {
+        return new ArrayList<>();
+    }
+
+    @Override
+    public List<CDTFrameBean> transmitYx() {
+        String logStr = JSON.toJSONString(this.yx);
+        log.info("遥信数据:{}", logStr);
+        List<CDTFrameBean> frameList = null;
+        try {
+            frameList = new ArrayList<>(1);
+            if (yx == null || yx.size() == 0) {
+                return frameList;
+            }
+            Set<Integer> yxKey = yx.keySet();
+            int min = Collections.min(yxKey);
+            int max = Collections.max(yxKey);
+            if (min % 32 != 0) {
+                min = min / 32 * 32;
+            }
+            List<BaseDateType> dataTypeList = new ArrayList<>();
+            Map<Integer, Boolean> val;
+            BooleanDataType booleanDataType;
+            for (int i = 0; 32 * i <= max; i++) {
+                val = new HashMap<>(32);
+                for (int j = 0; j < 32; j++) {
+                    if (yxKey.contains(min + 32 * i + j)) {
+                        val.put(min + 32 * i + j, this.yx.get(min + 32 * i + j));
+                    } else {
+                        val.put(min + 32 * i + j, false);
+                    }
+                }
+                booleanDataType = new BooleanDataType(val);
+                dataTypeList.add(booleanDataType);
+            }
+            CDTFrameBean cdtFrameBean = new CDTFrameBean(dataTypeList);
+            frameList.add(cdtFrameBean);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return frameList;
+    }
+
+
+    /**
+     * 构建遥测数据帧数组
+     *
+     * @param cdtType
+     * @param yc
+     * @return cdt的数据帧集合
+     */
+    private List<CDTFrameBean> buildYc(CDTType cdtType, Map<Integer, Integer> yc) {
+        String logStr = JSON.toJSONString(this.secondYc);
+        log.info("次要遥测:{}", logStr);
+        logStr = JSON.toJSONString(this.commonYc);
+        log.info("一般遥测:{}", logStr);
+        logStr = JSON.toJSONString(this.commonYc);
+        log.info("重要遥测:{}", logStr);
+        List<Integer> ycKey = new ArrayList<>(yc.keySet());
+        Collections.sort(ycKey);
+        int f;
+        Map<Integer, Integer> val;
+        List<CDTFrameBean> frameList = new ArrayList<>(1);
+        List<BaseDateType> dataTypeList = new ArrayList<>();
+        IntegerDataType integerDataType;
+        for (int i = 0; i < ycKey.size(); ) {
+            val = new HashMap<>(2);
+            f = ycKey.get(i);
+            if (f % 2 == 0) {
+                val.put(f, yc.get(f));
+                if (ycKey.contains(f + 1)) {
+                    val.put(f + 1, yc.get(f + 1));
+                    i += 2;
+                } else {
+                    val.put(f + 1, 0);
+                    i += 1;
+                }
+            } else {
+                val.put(f - 1, 0);
+                val.put(f, yc.get(f));
+                i += 1;
+            }
+            integerDataType = new IntegerDataType(val, null);
+            dataTypeList.add(integerDataType);
+        }
+        CDTFrameBean cdtFrameBean = new CDTFrameBean(dataTypeList);
+        cdtFrameBean.setCdtType(cdtType);
+        frameList.add(cdtFrameBean);
+        return frameList;
+    }
+
+    @Override
+    public void connected() {
+        this.cdtRtuDataSender.setTunnelStatus(TunnelStatus.LISTENSERIALSUCCESS);
+    }
+
+
+    @Override
+    public void disconnected() {
+        this.cdtRtuDataSender.setTunnelStatus(TunnelStatus.LISTENSERIALFAIL);
+    }
+}

+ 69 - 0
src/main/java/tunnelworker/workassist/iec104tcp/HandelBoolean.java

@@ -0,0 +1,69 @@
+package tunnelworker.workassist.iec104tcp;
+
+
+
+import container.ProtocolDataContainer;
+import entity.datapoint.GatherDataPoint;
+import wei.yigulu.iec104.annotation.AsduType;
+import wei.yigulu.iec104.apdumodel.Apdu;
+import wei.yigulu.iec104.asdudataframe.BooleanType;
+import wei.yigulu.iec104.asdudataframe.typemodel.IeBoolean;
+import wei.yigulu.iec104.asdudataframe.typemodel.InformationBodyAddress;
+import wei.yigulu.iec104.nettyconfig.Iec104HSMasterBuilder;
+import wei.yigulu.netty.BaseProtocolBuilder;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 遥信处理
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@AsduType(typeId = 1)
+public class HandelBoolean extends BooleanType {
+
+
+	private ProtocolDataContainer protocolDataContainerRedis = ProtocolDataContainer.getInstance();
+
+	/**
+	 * 处理短浮点数据
+	 *
+	 * @param apdu 传入的包文
+	 * @return 返回 响应
+	 */
+	@Override
+	public byte[][] handleAndAnswer(Apdu apdu) {
+		apdu.getLog().trace("----------处理遥信数据---------");
+		BaseProtocolBuilder builder = apdu.getIec104Builder();
+		if (builder != null && builder instanceof Iec104HSMasterBuilder) {
+			Map<Integer, GatherDataPoint> dataPoint = (Map<Integer, GatherDataPoint>) builder.getConfigInfoMap().get("104DataPoint");
+			HandelBoolean handelBoolean = (HandelBoolean) apdu.getAsdu().getDataFrame();
+			List<InformationBodyAddress> address = handelBoolean.getAddresses();
+			List<IeBoolean> datas = handelBoolean.getDatas();
+			int i = 0;
+			//存入共享服务端
+			if (apdu.getAsdu().getVsq().getSq() == 0) {
+				apdu.getLog().trace("------处理遥信单一寻址-----");
+				for (IeBoolean e : datas) {
+					if (dataPoint.containsKey(address.get(i).getAddress())) {
+						protocolDataContainerRedis.putGBoolean(dataPoint.get(address.get(i).getAddress()).getId(), e.isOn());
+					}
+					i++;
+				}
+			} else if (apdu.getAsdu().getVsq().getSq() == 1) {
+				apdu.getLog().trace("------处理遥信连续寻址-----");
+				i = address.get(0).getAddress();
+				for (IeBoolean e : datas) {
+					if (dataPoint.containsKey(i)) {
+						protocolDataContainerRedis.putGBoolean(dataPoint.get(i).getId(), e.isOn());
+					}
+					i++;
+				}
+			}
+		}
+		return null;
+	}
+
+}

+ 69 - 0
src/main/java/tunnelworker/workassist/iec104tcp/HandelNoQualityNormalizedInteger.java

@@ -0,0 +1,69 @@
+package tunnelworker.workassist.iec104tcp;
+
+
+
+import container.ProtocolDataContainer;
+import entity.datapoint.GatherDataPoint;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import wei.yigulu.iec104.annotation.AsduType;
+import wei.yigulu.iec104.apdumodel.Apdu;
+import wei.yigulu.iec104.asdudataframe.NoQualityNormalizedIntegerType;
+import wei.yigulu.iec104.asdudataframe.typemodel.InformationBodyAddress;
+import wei.yigulu.iec104.nettyconfig.Iec104HSMasterBuilder;
+import wei.yigulu.netty.BaseProtocolBuilder;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 没有品质位的归一化值
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@AsduType(typeId = 21)
+@Data
+@NoArgsConstructor
+public class HandelNoQualityNormalizedInteger extends NoQualityNormalizedIntegerType {
+
+
+	private ProtocolDataContainer protocolDataContainerRedis = ProtocolDataContainer.getInstance();
+
+
+	@Override
+	public byte[][] handleAndAnswer(Apdu apdu) throws Exception {
+		apdu.getLog().trace("----------处理无品质位的归一化值数据---------");
+		BaseProtocolBuilder builder = apdu.getIec104Builder();
+		if ((builder != null && builder instanceof Iec104HSMasterBuilder)) {
+			Map<Integer, GatherDataPoint> dataPoint = (Map<Integer, GatherDataPoint>) builder.getConfigInfoMap().get("104DataPoint");
+			HandelNoQualityNormalizedInteger handelNoQualityInt = (HandelNoQualityNormalizedInteger) apdu.getAsdu().getDataFrame();
+			List<InformationBodyAddress> address = handelNoQualityInt.getAddresses();
+			List<Integer> datas = handelNoQualityInt.getDatas();
+			int i = 0;
+			//存入共享服务端
+			if (apdu.getAsdu().getVsq().getSq() == 0) {
+				apdu.getLog().trace("------处理无品质位的归一化值单一寻址-----");
+				for (Integer e : datas) {
+					if (dataPoint.containsKey(address.get(i).getAddress())) {
+						protocolDataContainerRedis.putGNumber(dataPoint.get(address.get(i).getAddress()).getId(), BigDecimal.valueOf(e).multiply(dataPoint.get(address.get(i).getAddress()).getRatio()));
+					}
+					i++;
+				}
+			} else if (apdu.getAsdu().getVsq().getSq() == 1) {
+				apdu.getLog().trace("------处理无品质位的归一化值连续寻址-----");
+				i = address.get(0).getAddress();
+				for (Integer e : datas) {
+					if (dataPoint.containsKey(i)) {
+						protocolDataContainerRedis.putGNumber(dataPoint.get(i).getId(), BigDecimal.valueOf(e).multiply(dataPoint.get(i).getRatio()));
+					}
+					i++;
+				}
+			}
+		}
+		return null;
+	}
+
+
+}

+ 78 - 0
src/main/java/tunnelworker/workassist/iec104tcp/HandelNormalizedInteger.java

@@ -0,0 +1,78 @@
+package tunnelworker.workassist.iec104tcp;
+
+
+import container.ProtocolDataContainer;
+import entity.datapoint.GatherDataPoint;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import wei.yigulu.iec104.annotation.AsduType;
+import wei.yigulu.iec104.apdumodel.Apdu;
+import wei.yigulu.iec104.asdudataframe.NormalizedIntegerType;
+import wei.yigulu.iec104.asdudataframe.qualitydescription.IeMeasuredQuality;
+import wei.yigulu.iec104.asdudataframe.typemodel.InformationBodyAddress;
+import wei.yigulu.iec104.nettyconfig.Iec104HSMasterBuilder;
+import wei.yigulu.netty.BaseProtocolBuilder;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 没有品质位的归一化值
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@AsduType(typeId = 9)
+@Data
+@NoArgsConstructor
+public class HandelNormalizedInteger extends NormalizedIntegerType {
+
+
+	private ProtocolDataContainer protocolDataContainerRedis = ProtocolDataContainer.getInstance();
+
+
+	@Override
+	public byte[][] handleAndAnswer(Apdu apdu) throws Exception {
+		apdu.getLog().trace("----------处理的归一化值数据---------");
+		BaseProtocolBuilder builder = apdu.getIec104Builder();
+		if (builder != null && builder instanceof Iec104HSMasterBuilder) {
+			//所有master 视为统一的 处理逻辑---存入数据池
+			Map<Integer, GatherDataPoint> dataPoint = (Map<Integer, GatherDataPoint>) builder.getConfigInfoMap().get("104DataPoint");
+			HandelNormalizedInteger handelNoQualityInt = (HandelNormalizedInteger) apdu.getAsdu().getDataFrame();
+			List<InformationBodyAddress> address = handelNoQualityInt.getAddresses();
+			Map<IeMeasuredQuality, Integer> datas = handelNoQualityInt.getDatas();
+			int i = 0;
+			//存入共享服务端
+			if (apdu.getAsdu().getVsq().getSq() == 0) {
+				apdu.getLog().trace("------处理归一化值单一寻址-----");
+				for (Map.Entry<IeMeasuredQuality, Integer> e : datas.entrySet()) {
+					if (dataPoint.containsKey(address.get(i).getAddress())) {
+						protocolDataContainerRedis.putGNumber(dataPoint.get(address.get(i).getAddress()).getId(), BigDecimal.valueOf(e.getValue()).multiply(dataPoint.get(address.get(i).getAddress()).getRatio()));
+					}
+					i++;
+				}
+			} else if (apdu.getAsdu().getVsq().getSq() == 1) {
+				apdu.getLog().trace("------处理归一化值连续寻址-----");
+				i = address.get(0).getAddress();
+				for (Map.Entry<IeMeasuredQuality, Integer> e : datas.entrySet()) {
+					if (dataPoint.containsKey(i)) {
+						protocolDataContainerRedis.putGNumber(dataPoint.get(i).getId(), BigDecimal.valueOf(e.getValue()).multiply(dataPoint.get(i).getRatio()));
+					}
+					i++;
+				}
+			}
+		}
+		return null;
+	}
+
+
+	@Override
+	public String toString() {
+		return super.toString();
+	}
+
+
+}

+ 33 - 0
src/main/java/tunnelworker/workassist/iec104tcp/HandleNormalizationValueCommand.java

@@ -0,0 +1,33 @@
+package tunnelworker.workassist.iec104tcp;
+
+import lombok.extern.slf4j.Slf4j;
+import wei.yigulu.iec104.annotation.AsduType;
+import wei.yigulu.iec104.apdumodel.Apdu;
+import wei.yigulu.iec104.asdudataframe.NormalizationValueCommand;
+
+
+/**
+ * 处理归一化值命令的接收
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Slf4j
+@AsduType(typeId = 48)
+public class HandleNormalizationValueCommand extends NormalizationValueCommand {
+
+
+    @Override
+    public byte[][] handleAndAnswer(Apdu apdu) throws Exception {
+        NormalizationValueCommand dataFrame = (NormalizationValueCommand) apdu.getAsdu().getDataFrame();
+        dataFrame.getAddresses();
+        dataFrame.getVal();
+        String tunnelId = (String) apdu.getIec104Builder().getConfigInfoMap().get("tunnelId");
+        String tunnelName = (String) apdu.getIec104Builder().getConfigInfoMap().get("tunnelName");
+        log.info("通道{},接收到命令:{},命令类型:{},命令值为:{}" , tunnelName,dataFrame.getAddresses() , "归一化值",dataFrame.getVal());
+      //  ReceiveDispatchingCommand.getInstance().receiveInstruction(tunnelId, dataFrame.getAddresses().getAddress(), BigDecimal.valueOf(dataFrame.getVal().getValue()));
+        return super.handleAndAnswer(apdu);
+    }
+
+
+}

+ 66 - 0
src/main/java/tunnelworker/workassist/iec104tcp/HandleShortFloat.java

@@ -0,0 +1,66 @@
+package tunnelworker.workassist.iec104tcp;
+
+
+import container.ProtocolDataContainer;
+import entity.datapoint.GatherDataPoint;
+import wei.yigulu.iec104.annotation.AsduType;
+import wei.yigulu.iec104.apdumodel.Apdu;
+import wei.yigulu.iec104.asdudataframe.ShortFloatType;
+import wei.yigulu.iec104.asdudataframe.qualitydescription.IeMeasuredQuality;
+import wei.yigulu.iec104.asdudataframe.typemodel.InformationBodyAddress;
+import wei.yigulu.iec104.nettyconfig.Iec104HSMasterBuilder;
+import wei.yigulu.netty.BaseProtocolBuilder;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 处理短浮点
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@AsduType(typeId = 13)
+public class HandleShortFloat extends ShortFloatType {
+
+	private ProtocolDataContainer protocolDataContainerRedis = ProtocolDataContainer.getInstance();
+
+	/**
+	 * 处理短浮点数据
+	 */
+	@Override
+	public byte[][] handleAndAnswer(Apdu apdu) {
+		apdu.getLog().trace("----------处理短浮点数据---------");
+		BaseProtocolBuilder builder = apdu.getIec104Builder();
+		if (builder != null && builder instanceof Iec104HSMasterBuilder) {
+			Map<Integer, GatherDataPoint> dataPoint = (Map<Integer, GatherDataPoint>) builder.getConfigInfoMap().get("104DataPoint");
+			HandleShortFloat handleShortFloat = (HandleShortFloat) apdu.getAsdu().getDataFrame();
+			List<InformationBodyAddress> address = handleShortFloat.getAddresses();
+			Map<IeMeasuredQuality, Float> dates = handleShortFloat.getDatas();
+			int i = 0;
+			//存入数据池
+			if (apdu.getAsdu().getVsq().getSq() == 0) {
+				apdu.getLog().info("------处理短浮点单一寻址-----");
+				for (Map.Entry<IeMeasuredQuality, Float> e : dates.entrySet()) {
+					if (dataPoint.containsKey(address.get(i).getAddress())) {
+						protocolDataContainerRedis.putGNumber(dataPoint.get(address.get(i).getAddress()).getId(), BigDecimal.valueOf(e.getValue()).multiply(dataPoint.get(address.get(i).getAddress()).getRatio()));
+					}
+					i++;
+				}
+			} else if (apdu.getAsdu().getVsq().getSq() == 1) {
+				apdu.getLog().info("------处理短浮点连续寻址-----");
+				i = address.get(0).getAddress();
+				for (Map.Entry<IeMeasuredQuality, Float> e : dates.entrySet()) {
+					if (dataPoint.containsKey(i)) {
+						protocolDataContainerRedis.putGNumber(dataPoint.get(i).getId(), BigDecimal.valueOf(e.getValue()).multiply(dataPoint.get(i).getRatio()));
+					}
+					i++;
+				}
+			}
+
+		}
+		return null;
+	}
+
+}

+ 35 - 0
src/main/java/tunnelworker/workassist/iec104tcp/HandleShortFloatCommand.java

@@ -0,0 +1,35 @@
+package tunnelworker.workassist.iec104tcp;
+
+
+import lombok.extern.slf4j.Slf4j;
+import wei.yigulu.iec104.annotation.AsduType;
+import wei.yigulu.iec104.apdumodel.Apdu;
+import wei.yigulu.iec104.asdudataframe.ShortFloatCommand;
+
+import java.math.BigDecimal;
+
+/**
+ * 处理命令的接收
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@Slf4j
+@AsduType(typeId = 50)
+public class HandleShortFloatCommand extends ShortFloatCommand {
+
+
+    @Override
+    public byte[][] handleAndAnswer(Apdu apdu) throws Exception {
+        ShortFloatCommand shortFloatCommand = (ShortFloatCommand) apdu.getAsdu().getDataFrame();
+        shortFloatCommand.getAddresses();
+        shortFloatCommand.getVal();
+        String tunnelId = (String) apdu.getIec104Builder().getConfigInfoMap().get("tunnelId");
+        String tunnelName = (String) apdu.getIec104Builder().getConfigInfoMap().get("tunnelName");
+        log.info("通道{},接收到命令:{},命令类型:{},命令值为:{}" , tunnelName,shortFloatCommand.getAddresses() , "浮点遥调",shortFloatCommand.getVal());
+        //ReceiveDispatchingCommand.getInstance().receiveInstruction(tunnelId, shortFloatCommand.getAddresses().getAddress(), BigDecimal.valueOf(shortFloatCommand.getVal()));
+        return super.handleAndAnswer(apdu);
+    }
+
+
+}

+ 32 - 0
src/main/java/tunnelworker/workassist/iec104tcp/HandleSingleBooleanCommand.java

@@ -0,0 +1,32 @@
+package tunnelworker.workassist.iec104tcp;
+
+import lombok.extern.slf4j.Slf4j;
+import wei.yigulu.iec104.annotation.AsduType;
+import wei.yigulu.iec104.apdumodel.Apdu;
+import wei.yigulu.iec104.asdudataframe.SingleBooleanCommand;
+
+/**
+ * 处理命令的接收
+ *
+ * @author: xiuwei
+ * @version:
+ */
+@AsduType(typeId = 45)
+@Slf4j
+public class HandleSingleBooleanCommand extends SingleBooleanCommand {
+
+
+	@Override
+	public byte[][] handleAndAnswer(Apdu apdu) throws Exception {
+		SingleBooleanCommand singleBooleanCommand = (SingleBooleanCommand) apdu.getAsdu().getDataFrame();
+		singleBooleanCommand.getAddresses();
+		singleBooleanCommand.getVal();
+		String tunnelId = (String) apdu.getIec104Builder().getConfigInfoMap().get("tunnelId");
+		String tunnelName = (String) apdu.getIec104Builder().getConfigInfoMap().get("tunnelName");
+		log.info("通道{},接收到命令:{},命令类型:{},命令值为:{}" , tunnelName,singleBooleanCommand.getAddresses() , "单点遥控",singleBooleanCommand.getVal().getIecValue());
+		//ReceiveDispatchingCommand.getInstance().receiveInstruction(tunnelId, singleBooleanCommand.getAddresses().getAddress(), singleBooleanCommand.getVal().getIecValue());
+		return super.handleAndAnswer(apdu);
+	}
+
+
+}

+ 71 - 0
src/main/java/tunnelworker/workassist/iec104tcp/HandleTotalSummon.java

@@ -0,0 +1,71 @@
+package tunnelworker.workassist.iec104tcp;
+
+
+import entity.datagetter.BaseDataGetter;
+import wei.yigulu.iec104.annotation.AsduType;
+import wei.yigulu.iec104.apdumodel.Apdu;
+import wei.yigulu.iec104.asdudataframe.TotalSummonType;
+import wei.yigulu.iec104.nettyconfig.Iec104HSMasterBuilder;
+import wei.yigulu.iec104.nettyconfig.Iec104SlaverBuilder;
+import wei.yigulu.iec104.util.SendDataFrameHelper;
+import wei.yigulu.netty.BaseProtocolBuilder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 处理总召唤帧
+ *
+ * @author 修唯xiuwei
+ * @version 3.0
+ */
+@AsduType(typeId = 100)
+public class HandleTotalSummon extends TotalSummonType {
+
+
+    @Override
+    public byte[][] handleAndAnswer(Apdu apdu) throws Exception {
+        apdu.getLog().trace("----------响应总召唤---------");
+        BaseProtocolBuilder builder = apdu.getIec104Builder();
+
+        Map<String, Object> confMap = builder.getConfigInfoMap();
+        //当该帧是由 SlaverBuilder 或其子类接收到的  且为总召唤原因为激活
+        if (builder instanceof Iec104SlaverBuilder) {
+            if (apdu.getAsdu().getCot().getNot() == 6) {
+                //响应总召唤 激活确认帧
+                SendDataFrameHelper.sendTotalSummonFrame(apdu.getChannel(), apdu.getAsdu().getCommonAddress(), 7, builder.getLog());
+                //响应遥信
+                if (confMap.get("yx") != null) {
+                    Map<Integer, BaseDataGetter<Boolean>> yxDataPoints = (Map<Integer, BaseDataGetter<Boolean>>) confMap.get("yx");
+                    Map<Integer, Boolean> yx = new HashMap<>();
+                    yxDataPoints.forEach((k, v) -> {
+                        yx.put(k, v.getValue());
+                    });
+                    SendDataFrameHelper.sendYxDataFrame(apdu.getChannel(), yx, apdu.getAsdu().getCommonAddress(), 20, builder.getLog());
+                }
+                //响应遥测
+                if (confMap.get("yc") != null) {
+                    Map<Integer, BaseDataGetter<Number>> ycDataPoints = (Map<Integer, BaseDataGetter<Number>>) confMap.get("yc");
+                    Map<Integer, Number> yc = new HashMap<>();
+                    ycDataPoints.forEach((k, v) -> {
+                        yc.put(k, v.getValue());
+                    });
+                    SendDataFrameHelper.sendYcDataFrame(apdu.getChannel(), yc, apdu.getAsdu().getCommonAddress(), 20, builder.getLog());
+                }
+                //响应总召唤 激活停止帧
+                SendDataFrameHelper.sendTotalSummonFrame(apdu.getChannel(), apdu.getAsdu().getCommonAddress(), 8, builder.getLog());
+            } else if (apdu.getAsdu().getCot().getNot() == 9) {
+                SendDataFrameHelper.sendTotalSummonFrame(apdu.getChannel(), apdu.getAsdu().getCommonAddress(), 10, builder.getLog());
+            }
+        }
+        // 如果是master 接收到停止激活帧
+        else if (builder instanceof Iec104HSMasterBuilder) {
+            if (apdu != null && apdu.getAsdu() != null && apdu.getAsdu().getCot() != null && apdu.getAsdu().getCot().getNot() == 8) {
+                //响应总召唤 确认激活停止帧
+                SendDataFrameHelper.sendTotalSummonFrame(apdu.getChannel(), apdu.getAsdu().getCommonAddress(), 9, builder.getLog());
+            }
+        }
+        return null;
+    }
+
+}

+ 5 - 2
src/test/java/Test.java

@@ -7,8 +7,11 @@ public class Test {
     public static void main(String[] args) {
         PidController pidController = new PidController();
         PowerVariationCalculator pc=new PowerVariationCalculator();
-        for (int i = 0; i < 80; i=i+3) {
-            System.out.println(pc.calculate( pidController.calculate(BigDecimal.valueOf(i))));
+        for (double i = 50.183; i >50; i=i-0.001D) {
+            BigDecimal calculate = pidController.calculate(BigDecimal.valueOf(i));
+            System.out.println(calculate);
+            System.out.println(pc.calculate(calculate ));
+            System.out.println("-----------------------------------------------");
         }
     }
 }