hfxc226 2 жил өмнө
parent
commit
fc1ff8e4d8
47 өөрчлөгдсөн 3863 нэмэгдсэн , 1 устгасан
  1. 6 1
      platform-iec/pom.xml
  2. 180 0
      platform-iec/src/main/java/com/ydl/iec/iec104/common/BasicInstruction104.java
  3. 57 0
      platform-iec/src/main/java/com/ydl/iec/iec104/common/Iec104Constant.java
  4. 15 0
      platform-iec/src/main/java/com/ydl/iec/iec104/config/DefaultIec104Config.java
  5. 21 0
      platform-iec/src/main/java/com/ydl/iec/iec104/config/Iec104Config.java
  6. 29 0
      platform-iec/src/main/java/com/ydl/iec/iec104/core/CachedThreadPool.java
  7. 120 0
      platform-iec/src/main/java/com/ydl/iec/iec104/core/ControlManageUtil.java
  8. 268 0
      platform-iec/src/main/java/com/ydl/iec/iec104/core/Decoder104.java
  9. 94 0
      platform-iec/src/main/java/com/ydl/iec/iec104/core/Encoder104.java
  10. 59 0
      platform-iec/src/main/java/com/ydl/iec/iec104/core/Iec104ThreadLocal.java
  11. 172 0
      platform-iec/src/main/java/com/ydl/iec/iec104/core/ScheduledTaskPool.java
  12. 90 0
      platform-iec/src/main/java/com/ydl/iec/iec104/enums/QualifiersEnum.java
  13. 105 0
      platform-iec/src/main/java/com/ydl/iec/iec104/enums/TypeIdentifierEnum.java
  14. 42 0
      platform-iec/src/main/java/com/ydl/iec/iec104/enums/UControlEnum.java
  15. 148 0
      platform-iec/src/main/java/com/ydl/iec/iec104/message/MessageDetail.java
  16. 38 0
      platform-iec/src/main/java/com/ydl/iec/iec104/message/MessageInfo.java
  17. 32 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/Iec104Master.java
  18. 26 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/Iec104MasterFactory.java
  19. 34 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/Iec104Slave.java
  20. 24 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/Iec104SlaveFactory.java
  21. 21 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/BytesEncoder.java
  22. 14 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/ChannelHandler.java
  23. 26 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/ChannelHandlerImpl.java
  24. 40 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/Check104Handler.java
  25. 34 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/DataDecoder.java
  26. 46 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/DataEncoder.java
  27. 32 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/DataHandler.java
  28. 62 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/SysSframeHandler.java
  29. 52 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/Unpack104Handler.java
  30. 60 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/master/Iec104ClientInitializer.java
  31. 57 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/master/Iec104TcpClientMaster.java
  32. 65 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/master/handler/Iec104ClientHandler.java
  33. 83 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/master/handler/SysUframeClientHandler.java
  34. 62 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/slave/Iec104ServerInitializer.java
  35. 85 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/slave/Iec104TcpServerSlave.java
  36. 104 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/slave/handler/Iec104TcpSlaveHandler.java
  37. 89 0
      platform-iec/src/main/java/com/ydl/iec/iec104/server/slave/handler/SysUframeServerHandler.java
  38. 251 0
      platform-iec/src/main/java/com/ydl/iec/util/ByteUtil.java
  39. 252 0
      platform-iec/src/main/java/com/ydl/iec/util/Iec104Util.java
  40. 52 0
      platform-iec/src/test/java/com/ydl/iec/iec104/core/Decoder104Test.java
  41. 19 0
      platform-iec/src/test/java/com/ydl/iec/iec104/server/master/Iec104TcpMasterClientTest.java
  42. 79 0
      platform-iec/src/test/java/com/ydl/iec/iec104/server/master/MasterSysDataHandler.java
  43. 34 0
      platform-iec/src/test/java/com/ydl/iec/iec104/server/slave/Iec104TcpServerSlaveTest.java
  44. 51 0
      platform-iec/src/test/java/com/ydl/iec/iec104/server/slave/SysDataHandler.java
  45. 394 0
      platform-iec/src/test/java/com/ydl/iec/iec104/util/Encoder104UtilTest.java
  46. 78 0
      platform-iec/src/test/java/com/ydl/iec/util/ByteUtilTest.java
  47. 161 0
      platform-iec/src/test/java/com/ydl/iec/util/RuleDetail104utilTest.java

+ 6 - 1
platform-iec/pom.xml

@@ -54,7 +54,12 @@
             <type>pom</type>
             <scope>import</scope>
         </dependency>
-
+        <!--netty-->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+            <version>4.1.42.Final</version>
+        </dependency>
         <!--测试依赖-->
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 180 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/common/BasicInstruction104.java

@@ -0,0 +1,180 @@
+package com.ydl.iec.iec104.common;
+
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.message.MessageInfo;
+import com.ydl.iec.iec104.enums.QualifiersEnum;
+import com.ydl.iec.iec104.enums.TypeIdentifierEnum;
+import com.ydl.iec.iec104.enums.UControlEnum;
+import com.ydl.iec.util.ByteUtil;
+import com.ydl.iec.util.Iec104Util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 104 规约的基本指令封装
+* @ClassName: BasicInstruction104  
+* @Description: 返回指定的指令 
+* @author YDL 
+ */
+
+public class BasicInstruction104 {
+	// 68040B 00 00 00
+	/**
+	 * 初始确认指令
+	 */
+	public static final byte[] STARTDT_YES = new byte[] {0x68, 0x04, 0x0B, 0x00, 0x00, 0x00};
+	
+	/**
+	 * 链路启动指令
+	 */
+	public static final byte[] STARTDT = new byte[] {0x68, 0x04, 0x07, 0x00, 0x00, 0x00};
+	
+	
+	/**
+	 * 测试确认
+	 */
+	public static final byte[] TESTFR_YES = new byte[] {0x68, 0x04, (byte) 0x83, 0x00, 0x00, 0x00};
+	 
+	/**
+	 * 测试命令指令
+	 */
+	public static final byte[] TESTFR = new byte[] {0x68, 0x04, (byte) 0x43, 0x00, 0x00, 0x00};
+	
+	
+	/**
+	 * 停止确认
+	 */
+	public static final byte[] STOPDT_YES = new byte[] {0x68, 0x04, 0x23, 0x00, 0x00, 0x00};
+	
+	
+	
+	
+	
+	/**
+	 * 
+	* @Title: getGeneralCallRuleDetail104  
+	* @Description: 总召唤指令 
+	* @param @return
+	* @param @throws IOException 
+	* @return MessageDetail
+	* @throws
+	 */
+	public static MessageDetail getGeneralCallRuleDetail104() {
+		TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.generalCall;
+		int sq = 0;
+		boolean isContinuous = sq == 0 ? false : true;
+		// 接收序号
+		short accept = 0;
+		// 发送序号
+		short send = 0;
+		byte[] control = Iec104Util.getIcontrol(accept, send);
+		// 传输原因
+		short transferReason = 6;
+		boolean isTest = false;
+		boolean isPn = true;
+		// 终端地址 实际发生的时候会被替换
+		short terminalAddress = 1;
+		// 消息地址 总召唤地址为0
+		int messageAddress = 0;
+		QualifiersEnum qualifiers = QualifiersEnum.generalCallGroupingQualifiers;
+		List<MessageInfo> messages = new ArrayList<>();
+//		MessageInfo message = new MessageInfo();
+//		message.setQualifiersType(qualifiers);
+//		message.setMessageInfos(new byte[] {});
+//		messages.add(message);
+		MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPn, transferReason,
+				terminalAddress, messageAddress, messages, null, qualifiers);
+		return ruleDetail104;
+	}
+	
+	
+	/**
+	 * 
+	* @Title: getYesGeneralCallRuleDetail104  
+	* @Description: 总召唤确认指令 
+	* @return 
+	* @return MessageDetail
+	* @throws
+	 */
+	public static MessageDetail getYesGeneralCallRuleDetail104() {
+		TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.generalCall; 
+		 //SQ=0 length =1
+		int sq = 0;
+		boolean isContinuous = sq == 0 ? false : true;
+		// 接收序号
+		short accept = 0;
+		// 发送序号
+		short send = 0;
+		byte[] control = Iec104Util.getIcontrol(accept, send);
+		// 传输原因
+		short transferReason = 7;
+		// true:1 ; false : 0
+		boolean isTest = false;
+		// true:0 false;1
+		boolean isPN = true;
+		
+		short terminalAddress = 1;
+		// 消息地址 总召唤地址为0
+		int messageAddress = 0;
+		
+		QualifiersEnum qualifiers = QualifiersEnum.generalCallGroupingQualifiers;
+		List<MessageInfo> messages = new ArrayList<>();
+//		MessageInfo message = new MessageInfo();
+//		message.setQualifiersType(qualifiers);
+//		message.setMessageInfos(new byte[] {});
+//
+//		messages.add(message);
+		MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason,
+				terminalAddress, messageAddress, messages, null, qualifiers);
+		return ruleDetail104;
+	}
+	
+	
+	/**
+	 * 
+	* @Title: getEndGeneralCallRuleDetail104  
+	* @Description: 总召唤结束指令 
+	* @return 
+	* @return MessageDetail
+	* @throws
+	 */
+	public static MessageDetail getEndGeneralCallRuleDetail104() {
+		TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.generalCall; 
+		 //SQ=0 length =1
+		int sq = 0;
+		boolean isContinuous = sq == 0 ? false : true;
+		// 接收序号
+		short accept = 1;
+		// 发送序号
+		short send = 4;
+		byte[] control = Iec104Util.getIcontrol(accept, send);
+		// 传输原因
+		short transferReason = 0x0A;
+		// true:1 ; false : 0
+		boolean isTest = false;
+		// true:0 false;1
+		boolean isPN = true;
+		
+		short terminalAddress = 1;
+		// 消息地址 总召唤地址为0
+		int messageAddress = 0;
+		// 老板限定词
+		QualifiersEnum qualifiers = QualifiersEnum.generalCallGroupingQualifiers;
+		List<MessageInfo> messages = new ArrayList<>();
+//		MessageInfo message = new MessageInfo();
+//		message.setQualifiersType(qualifiers);
+//		message.setMessageInfos(new byte[] {});
+//
+//		messages.add(message);
+		MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason,
+				terminalAddress, messageAddress, messages, null, qualifiers);
+		return ruleDetail104;
+	}
+
+	public static MessageDetail getInitRuleDetail104() {
+		byte[] control = ByteUtil.intToByteArray(UControlEnum.STARTDT.getValue());
+		MessageDetail ruleDetail104 = new MessageDetail(control);
+		return ruleDetail104;
+	}
+}

+ 57 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/common/Iec104Constant.java

@@ -0,0 +1,57 @@
+package com.ydl.iec.iec104.common;
+
+/**
+ * 
+* @ClassName: Iec104Constant  
+* @Description: TODO 
+* @author YDL 
+ */
+public class Iec104Constant {
+	
+	/**
+	 * 开始字符
+	 */
+	public static final  byte  HEAD_DATA = 0x68;
+	
+	/**
+	 * 控制域长度
+	 */
+	public static final  byte CPNTROL_LENGTH = 0x04;
+	
+	/**
+	 * APCI 长度
+	 */
+	public static final byte APCI_LENGTH = 0x06;
+
+	/**
+	 * APCI 中 发送序号低位坐标
+	 */
+	public static final int ACCEPT_LOW_INDEX = 2;
+	
+	/**
+	 * APCI 中 发送序号高位坐标
+	 */
+	public static final int ACCEPT_HIGH_INDEX = 3;
+
+
+	/**
+	 *最大接收序号
+	 */
+	public static final Short SEND_MAX = 32767;
+
+	/**
+	 * 最小接收序号
+	 */
+	public static final Short SEND_MIN = 0;
+
+
+//	/**
+//	 *
+//	 */
+//	public static final short FRAME_SUM_MAX  = 1;
+//
+//	/**
+//	 * 终端地址
+//	 */
+//	public static final short TERMINNAL_ADDRESS = 1;
+}

+ 15 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/config/DefaultIec104Config.java

@@ -0,0 +1,15 @@
+package com.ydl.iec.iec104.config;
+
+import lombok.Data;
+
+/**
+ * 默认的配置
+ */
+@Data
+public class DefaultIec104Config extends Iec104Config {
+
+    public DefaultIec104Config() {
+        setFrameAmountMax((short) 1);
+        setTerminnalAddress((short) 1);
+    }
+}

+ 21 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/config/Iec104Config.java

@@ -0,0 +1,21 @@
+package com.ydl.iec.iec104.config;
+
+import lombok.Data;
+
+/**
+ * 104规约的配置
+ */
+@Data
+public class Iec104Config {
+
+    /**
+     * 接收到帧的数量到该值就要发一个确认帧
+     */
+    private short frameAmountMax;
+
+
+    /**
+     * 终端地址
+     */
+    private short terminnalAddress;
+}

+ 29 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/core/CachedThreadPool.java

@@ -0,0 +1,29 @@
+package com.ydl.iec.iec104.core;
+
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * 线程池
+ */
+public final class CachedThreadPool {
+
+    private  static CachedThreadPool cachedThreadPool  = new CachedThreadPool();
+    private ExecutorService executorService;
+
+
+    private CachedThreadPool() {
+        executorService = Executors.newCachedThreadPool();
+    }
+
+
+    public static CachedThreadPool getCachedThreadPool() {
+        return  cachedThreadPool;
+    }
+
+    public void execute(Runnable runnable) {
+        executorService.execute(runnable);
+    }
+
+}

+ 120 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/core/ControlManageUtil.java

@@ -0,0 +1,120 @@
+package com.ydl.iec.iec104.core;
+
+import com.ydl.iec.iec104.common.Iec104Constant;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.util.Iec104Util;
+import io.netty.channel.ChannelHandlerContext;
+
+/**
+ * 控制域的管理工具
+ */
+public class ControlManageUtil {
+
+	/**
+	 *  发送序号
+	 */
+	private Short send;
+
+	/**
+	 * 接收序号
+	 */
+	private Short accept;
+
+	/**
+	 * 接收到帧的数量
+	 */
+	private Short frameAmount;
+
+	/**
+	 *  发送S帧的锁
+	 */
+	private Boolean sendSframeLock;
+
+	/**
+	 * 接收到帧的数量最大阀值
+	 */
+
+	private short frameAmountMax;
+
+	/**
+	 * 发送消息句柄
+	 */
+	private ChannelHandlerContext ctx;
+	
+
+	
+	public ControlManageUtil(ChannelHandlerContext ctx) {
+		send = 0;
+		accept = 0;
+		frameAmount = 0;
+		sendSframeLock = true;
+		frameAmountMax = 1;
+		this.ctx = ctx;
+	}
+
+	/**
+	 * 启动S发送S确认帧 的任务
+	 */
+	public void startSendFrameTask() {
+		Runnable runnable = () -> {
+			while (true) {
+				try {
+					synchronized (sendSframeLock) {
+						if (frameAmount >= frameAmountMax) {
+							// 查过最大帧 的数量就要发送一个确认帧出去
+							byte[] control = Iec104Util.getScontrol(accept);
+							MessageDetail ruleDetail104 = new MessageDetail(control);
+							ctx.channel().writeAndFlush(Encoder104.encoder(ruleDetail104));
+							frameAmount = 0;
+						}
+						sendSframeLock.wait();
+					}
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		};
+		CachedThreadPool.getCachedThreadPool().execute(runnable);
+	}
+	
+	
+	/**
+	 * 返回当前的发送序号
+	 */
+	public short getSend() {
+		synchronized (send) {
+			short sendRule = this.send;
+			this.send++;
+			if (send > Iec104Constant.SEND_MAX) {
+				send = Iec104Constant.SEND_MIN;
+			}
+			return sendRule;
+		}
+	}
+	public short getAccept() {
+		return accept;
+	} 
+	
+	/**
+	 * 
+	* @Title: setAccept
+	* @Description: 设置接收序号
+	* @param lastAccept
+	 */
+	public void setAccept(short lastAccept) {
+		synchronized (sendSframeLock) {
+			this.accept = lastAccept;
+			frameAmount++;
+			if (frameAmount >= frameAmountMax) {
+				this.accept = lastAccept;
+				sendSframeLock.notifyAll();
+			}
+		}
+	}
+
+
+	public ControlManageUtil  setFrameAmountMax(short frameAmountMax) {
+		this.frameAmountMax = frameAmountMax;
+		return  this;
+	}
+}

+ 268 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/core/Decoder104.java

@@ -0,0 +1,268 @@
+package com.ydl.iec.iec104.core;
+
+import com.ydl.iec.util.Iec104Util;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.message.MessageInfo;
+import com.ydl.iec.iec104.enums.QualifiersEnum;
+import com.ydl.iec.iec104.enums.TypeIdentifierEnum;
+import com.ydl.iec.util.ByteUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 
+* @ClassName: Decoder104
+* @Description: 104 协议转码工具 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class Decoder104 {
+
+	/**
+	 * 将bytes 转换成指定的数据结构
+	 * @param bytes
+	 * @return
+	 */
+	public static MessageDetail encoder(byte[] bytes) {
+		MessageDetail ruleDetail104 = new MessageDetail();
+		int index = 0;
+		ruleDetail104.setStart(bytes[index++]);
+		ruleDetail104.setApuuLength(bytes[index++] & 0xFF);
+		ruleDetail104.setControl(ByteUtil.getByte(bytes, index, 4));
+		index += 4;
+		if (ruleDetail104.getApuuLength() <= 4) {
+			ruleDetail104.setMessages(new ArrayList<>());
+			// 如果只有APCI 就此返回
+			return ruleDetail104;
+		} 
+		// 下面是返回ASDU的结构
+		ruleDetail104.setTypeIdentifier(TypeIdentifierEnum.getTypeIdentifierEnum(bytes[index++]));
+		// 添加可变结构限定词
+		Iec104Util.setChanged(ruleDetail104, bytes[index++]);
+		ruleDetail104.setTransferReason(ByteUtil.byteArrayToShort(ByteUtil.getByte(bytes, index, 2)));
+		index += 2;
+		// 
+		ruleDetail104.setTerminalAddress(Iec104Util.getTerminalAddressShort(ByteUtil.getByte(bytes, index, 2)));
+		index += 2;
+		Iec104Util.setMeaageAttribute(ruleDetail104);
+		setMessage(ruleDetail104, bytes, index);
+		return ruleDetail104;
+	}
+
+	/**
+	 * 
+	* @Title: setMessage  
+	* @Description: 对消息进行编码  
+	* @param @param ruleDetail104
+	* @param @param bytes
+	* @param @param index 
+	* @return void   
+	* @throws
+	 */
+	public static void setMessage(MessageDetail ruleDetail104, byte[] bytes, int index) {
+		int mesageIndex = index;
+		if (ruleDetail104.isContinuous()) {
+			setContinuoustMessage(ruleDetail104, bytes, mesageIndex);
+		}  else {
+			setNoContinuoustMessage(ruleDetail104, bytes, mesageIndex);
+		}
+	} 
+	
+	
+	/**
+	 * 
+	* @Title: setContinuoustMessage  
+	* @Description: 设置连续地址的消息 
+	* @param ruleDetail104
+	* @param bytes
+	* @param index 
+	* @return void   
+	* @throws
+	 */
+	public static void setContinuoustMessage(MessageDetail ruleDetail104, byte[] bytes, int index) {
+		List<MessageInfo> messages = new ArrayList<>();
+		int mesageIndex = index;
+		// 连续的 前三个字节是地址 
+		// 如果是地址联系的只需要设置一个初始地址就可以了
+		// TODO 此处不处理地址
+		int messageAddress = Iec104Util.messageAddressToInt(ByteUtil.getByte(bytes, mesageIndex, 3));
+		ruleDetail104.setMessageAddress(messageAddress);
+		mesageIndex += 3;
+		if (ruleDetail104.isMessage()) {
+			// 获取每个消息的长度
+			int messageLength = getMessageLength(ruleDetail104);
+			int messageSize = 0;
+			while (messageSize < ruleDetail104.getMeasgLength()) {
+				MessageInfo messageObj = new MessageInfo();
+				messageObj.setMessageAddress(messageAddress);
+				byte[] messageInfos = ByteUtil.getByte(bytes, mesageIndex, messageLength);
+				mesageIndex += messageLength;
+				messageObj.setMessageInfos(messageInfos);
+				//对数据的值进行解析
+				setMessageValue(ruleDetail104, messageObj);
+
+				if (ruleDetail104.isQualifiers() && TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) {
+					// 判断是否有限定词
+					// 0(每个信息元素后缀1个字节)
+					ruleDetail104.setQualifiersType(QualifiersEnum.getQualifiersEnum(ruleDetail104.getTypeIdentifier(), bytes[mesageIndex++]));
+				}
+
+				messageSize++;
+				messageAddress++;
+				messages.add(messageObj);
+			}
+		}
+
+
+		if (ruleDetail104.isQualifiers() &&  !TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) {
+			// 判断是否有限定词
+			ruleDetail104.setQualifiersType(QualifiersEnum.getQualifiersEnum(ruleDetail104.getTypeIdentifier(), bytes[mesageIndex++]));
+		}
+
+
+		if (ruleDetail104.isTimeScaleExit()) {
+			ruleDetail104.setTimeScale(ByteUtil.byte2Hdate(ByteUtil.getByte(bytes, mesageIndex, 7)));
+		}
+		ruleDetail104.setMessages(messages);
+	} 
+	
+	
+	/**
+	 * 
+	* @Title: setNoContinuoustMessage  
+	* @Description: 设置不连续地址的消息 
+	* @param ruleDetail104
+	* @param bytes
+	* @param index 
+	* @return void   
+	* @throws
+	 */
+	public static void setNoContinuoustMessage(MessageDetail ruleDetail104, byte[] bytes, int index) {
+		List<MessageInfo> messages = new ArrayList<>();
+		int mesageIndex = index;
+		// 获取每个消息的长度
+		int messageLength = getMessageLength(ruleDetail104);
+		int messageSize = 0;
+		while (messageSize < ruleDetail104.getMeasgLength()) {
+			MessageInfo messageObj = new MessageInfo();
+			// 消息地址
+			messageObj.setMessageAddress(Iec104Util.messageAddressToInt(ByteUtil.getByte(bytes, mesageIndex, 3)));
+			mesageIndex += 3;
+			
+			if (ruleDetail104.isMessage()) {
+				// 消息集合
+				byte[] messageInfos = ByteUtil.getByte(bytes, mesageIndex, messageLength);
+				mesageIndex += messageLength;
+				messageObj.setMessageInfos(messageInfos);
+				//对数据的值进行解析
+				setMessageValue(ruleDetail104, messageObj);
+			} else {
+				messageObj.setMessageInfos(new byte[] {});
+			}
+
+			if (ruleDetail104.isQualifiers() &&  TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) {
+				// 判断是否有限定词
+				// 0(每个信息元素后缀1个字节)
+				ruleDetail104.setQualifiersType(QualifiersEnum.getQualifiersEnum(ruleDetail104.getTypeIdentifier(), bytes[mesageIndex++]));
+			}
+
+			messageSize++;
+			messages.add(messageObj);
+		}
+
+
+		if (ruleDetail104.isQualifiers() && !TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) {
+			// 判断是否有限定词
+			ruleDetail104.setQualifiersType(QualifiersEnum.getQualifiersEnum(ruleDetail104.getTypeIdentifier(), bytes[mesageIndex++]));
+		}
+
+
+		if (ruleDetail104.isTimeScaleExit()) {
+			ruleDetail104.setTimeScale(ByteUtil.byte2Hdate(ByteUtil.getByte(bytes, mesageIndex, 7)));
+		}
+
+		ruleDetail104.setMessages(messages);
+	}
+
+	/**
+	 * 根据类型对数据的值进行解析
+	 * */
+	private static void setMessageValue(MessageDetail ruleDetail104, MessageInfo messageObj) {
+		switch (ruleDetail104.getTypeIdentifier().getValue()) {
+			case  0x09:
+				// 遥测 测量值 归一化值 遥测
+				break;
+			case  0x0B:
+				// 遥测 测量值  标度化值 遥测
+				break;
+			case  0x66:
+				// 读单个参数
+				break;
+			case (byte) 0x84:
+				//  读多个参数
+				break;
+			case  0x30:
+				// 预置单个参数命令
+				break;
+			case (byte) 0x88:
+				// 预置多个个参数
+				break;
+			default :
+		}
+	}
+
+
+	/**
+	 * 根据类型标识返回消息长度
+	 */
+	private static int getMessageLength(MessageDetail ruleDetail104) {
+		return ruleDetail104.getTypeIdentifier().getMessageLength();
+//		int messageLength = 0;
+//		switch (ruleDetail104.getTypeIdentifier().getValue()) {
+//			case  0x01:
+//				// 单点遥信
+//				messageLength = 1;
+//				break;
+//			case  0x09:
+//				// 遥测 测量值 归一化值 遥测
+//				messageLength = 2;
+//				break;
+//			case  0x0B:
+//				// 遥测 测量值  标度化值 遥测
+//				messageLength =  2;
+//				break;
+//			case  0x0D:
+//				// 测量值 短浮点数 遥测
+//				messageLength = 4;
+//				break;
+//			case  0x0F:
+//				// 测量值 短浮点数 遥脉
+//				messageLength = 4;
+//				break;
+//			case  0x66:
+//				// 读单个参数
+//				messageLength = 4;
+//				break;
+//			case (byte) 0x84:
+//				//  读多个参数
+//				messageLength = 4;
+//				break;
+//			case  0x30:
+//				// 预置单个参数命令
+//				messageLength = 4;
+//				break;
+//			case (byte) 0x88:
+//				// 预置多个个参数
+//				messageLength = 4;
+//				break;
+//			case (byte) 0xCE:
+//				// 预置多个个参数
+//				messageLength = 4;
+//				break;
+//			default :
+//				messageLength = 0;
+//		}
+//		return messageLength;
+	}
+}

+ 94 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/core/Encoder104.java

@@ -0,0 +1,94 @@
+package com.ydl.iec.iec104.core;
+
+import com.ydl.iec.iec104.enums.QualifiersEnum;
+import com.ydl.iec.iec104.enums.TypeIdentifierEnum;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.message.MessageInfo;
+import com.ydl.iec.util.ByteUtil;
+import com.ydl.iec.util.Iec104Util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ *
+ * @author Admin
+ *
+ */
+public class Encoder104 {
+
+
+	public static byte[] encoder(MessageDetail ruleDetail104) throws IOException {
+		Iec104Util.setMeaageAttribute(ruleDetail104);
+		ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+		bytes.write(ruleDetail104.getStart());
+		byte[]  apduBytes = getApduBytes(ruleDetail104);
+		int messageLen =  apduBytes.length;
+		ruleDetail104.setApuuLength(messageLen);
+		bytes.write((byte) messageLen);
+		bytes.write(apduBytes);
+		return bytes.toByteArray();
+	}
+	
+	private static byte[] getApduBytes(MessageDetail ruleDetail104) throws IOException {
+		ByteArrayOutputStream bOutput = new ByteArrayOutputStream();
+		//  控制域
+		bOutput.write(ruleDetail104.getControl());
+		if (ruleDetail104.getTypeIdentifier() == null) {
+			// U帧或者S帧
+			return bOutput.toByteArray();
+		} 
+		// 类型标识
+		bOutput.write((byte) ruleDetail104.getTypeIdentifier().getValue());
+		// 可变结构限定词
+		bOutput.write(Iec104Util.getChangedQualifiers(ruleDetail104));
+		// 传输原因
+		bOutput.write(ByteUtil.shortToByteArray(ruleDetail104.getTransferReason()));
+		// 终端地址
+		bOutput.write((Iec104Util.getTerminalAddressByte(ruleDetail104.getTerminalAddress())));
+//		如果是是连续的则数据地址 只需要在开头写以后的数据单元就不需要再写了
+		if (ruleDetail104.isContinuous()) {
+			bOutput.write(Iec104Util.intToMessageAddress(ruleDetail104.getMessageAddress()));
+			// 地址只取三个字节
+			if (ruleDetail104.isMessage()) {
+				for (MessageInfo ruleDetail104Message : ruleDetail104.getMessages()) {
+					bOutput.write(ruleDetail104Message.getMessageInfos());
+					if (ruleDetail104.isQualifiers() &&  TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) {
+						// 0(每个信息元素后缀1个字节)
+						bOutput.write(ruleDetail104.getQualifiersType().getValue());
+					}
+
+				}
+			}
+
+			if (ruleDetail104.isQualifiers() && !TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) {
+				bOutput.write(ruleDetail104.getQualifiersType().getValue());
+			}
+
+			if (ruleDetail104.isTimeScaleExit()) {
+				bOutput.write(ByteUtil.date2Hbyte(ruleDetail104.getTimeScale()));
+			} 
+		} else {
+			for (MessageInfo ruleDetail104Message : ruleDetail104.getMessages()) {
+				bOutput.write(Iec104Util.intToMessageAddress(ruleDetail104Message.getMessageAddress()));
+				if (ruleDetail104.isMessage()) {
+					bOutput.write(ruleDetail104Message.getMessageInfos());
+				}
+				if (ruleDetail104.isQualifiers() && TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) {
+					// 0(每个信息元素后缀1个字节)
+					bOutput.write(ruleDetail104.getQualifiersType().getValue());
+				}
+			}
+
+			if (ruleDetail104.isQualifiers() && !TypeIdentifierEnum.isTelemetry(ruleDetail104.getTypeIdentifier())) {
+				bOutput.write(ruleDetail104.getQualifiersType().getValue());
+			}
+
+			if (ruleDetail104.isTimeScaleExit()) {
+				bOutput.write(ByteUtil.date2Hbyte(ruleDetail104.getTimeScale()));
+			}
+		}
+		return bOutput.toByteArray();
+	}
+
+}

+ 59 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/core/Iec104ThreadLocal.java

@@ -0,0 +1,59 @@
+package com.ydl.iec.iec104.core;
+
+
+import com.ydl.iec.iec104.config.Iec104Config;
+
+/**
+ * @ClassName:  Iec104ThreadLocal
+ * @Description:  线程变量管理
+ * @author: YDL
+ * @date:   2020年5月19日 上午10:48:57
+ */
+public class Iec104ThreadLocal {
+	
+	/**
+	 * 定时发送启动链指令 测试链指令 
+	 */
+	private static ThreadLocal<ScheduledTaskPool> scheduledTaskPoolThreadLocal = new ThreadLocal<>();
+	
+	/**
+	 * 返回 发送序号 和接收序号  定时发送S帧
+	 */
+	private static ThreadLocal<ControlManageUtil> controlPoolThreadLocal = new ThreadLocal<>();
+
+	/**
+	 * 存放相关配置文件
+	 */
+	private static ThreadLocal<Iec104Config> iec104ConfigThreadLocal = new ThreadLocal<>();
+
+
+
+
+	public static void setScheduledTaskPool(ScheduledTaskPool scheduledTaskPool) {
+		scheduledTaskPoolThreadLocal.set(scheduledTaskPool);
+	}
+
+	public  static ScheduledTaskPool getScheduledTaskPool() {
+		return scheduledTaskPoolThreadLocal.get();
+	}
+
+	public static void setControlPool(ControlManageUtil controlPool) {
+		controlPoolThreadLocal.set(controlPool);
+	}
+
+	public  static ControlManageUtil getControlPool() {
+		return controlPoolThreadLocal.get();
+	}
+
+
+	public  static Iec104Config getIec104Conig() {
+		return iec104ConfigThreadLocal.get();
+	}
+
+
+	public  static void setIec104Config(Iec104Config iec104Confiig) {
+		iec104ConfigThreadLocal.set(iec104Confiig);
+	}
+
+
+}

+ 172 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/core/ScheduledTaskPool.java

@@ -0,0 +1,172 @@
+package com.ydl.iec.iec104.core;
+
+import com.ydl.iec.iec104.common.BasicInstruction104;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ *  这是一个定时任务管理池
+ * @ClassName:  ScheduledTaskPool   
+ * @Description: 
+ * @author: YDL
+ * @date:   2020年5月19日 上午10:47:15
+ */
+@Slf4j
+public class ScheduledTaskPool {
+
+	private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);
+
+	/**
+	 * 发送指令
+	 */
+	private ChannelHandlerContext ctx;
+	/**
+	 * 发送启动指令的线程
+	 */
+	private Thread sendStatrThread;
+	/**
+	 * 循环发送启动指令线程的状态
+	 */
+	private Boolean sendStatrStatus = false;
+	/**
+	 * 发送测试指令的线程 类似心跳
+	 */
+	private Thread sendTestThread;
+	/**
+	 * 发送测试指令线程的状态
+	 */
+	private Boolean sendTestStatus = false;
+	/**
+	 * 发送总召唤指令状态
+	 */
+	private Boolean senGeneralCallStatus = false;
+	/**
+	 * 启动指令收到确认后固定时间内发送总召唤指令
+	 */
+	private Thread generalCallTThread;
+
+	public ScheduledTaskPool(ChannelHandlerContext ctx) {
+		this.ctx = ctx;
+	}
+
+	/**
+	 *
+	* @Title: sendStatrFrame
+	* @Description: 发送启动帧
+	 */
+	public void sendStatrFrame() {
+		synchronized (sendStatrStatus) {
+			if (sendStatrThread != null) {
+				sendStatrStatus = true;
+				sendStatrThread.start();
+			} else if (sendStatrThread == null) {
+				sendStatrStatus = true;
+				sendStatrThread = new Thread(new Runnable() {
+					@Override
+					public void run() {
+						while (sendStatrStatus) {
+							try {
+								ctx.channel().writeAndFlush(BasicInstruction104.STARTDT);
+								LOGGER.info("发送启动链路指令");
+								Thread.sleep(5000);
+							} catch (Exception e) {
+								e.printStackTrace();
+							}
+						}
+					}
+				});
+				sendStatrThread.start();
+			}
+		}
+	}
+
+	/**
+	 *
+	* @Title: stopSendStatrFrame
+	* @Description: 停止发送确认帧
+	 */
+	public void stopSendStatrFrame() {
+		if (sendStatrThread != null) {
+			sendStatrStatus = false;
+		}
+	}
+
+	
+	/**
+	 * 
+	* @Title: sendTestFrame
+	* @Description: 发送测试帧
+	 */
+	public void sendTestFrame() {
+		synchronized (sendTestStatus) {
+			if (sendTestThread != null && sendTestThread.getState() == Thread.State.TERMINATED) {
+				sendTestStatus = true;
+				sendTestThread.start();
+			} else if (sendTestThread == null) {
+				sendTestStatus = true;
+				sendTestThread = new Thread(new Runnable() {
+					@Override
+					public void run() {
+						while (sendTestStatus) {
+							try {
+								LOGGER.info("发送测试链路指令");
+								ctx.channel().writeAndFlush(BasicInstruction104.TESTFR);
+								Thread.sleep(5000);
+							} catch (Exception e) {
+								e.printStackTrace();
+							}
+						}
+					}
+				});
+				sendTestThread.start();
+			}
+		}
+	}
+	
+	/**
+	 *
+	* @Title: stopSendTestFrame
+	* @Description: 停止发送测试帧
+	 */
+	public void stopSendTestFrame() {
+		if (sendTestThread != null) {
+			sendTestStatus = false;
+		}
+	}
+
+	/**
+	 *
+	* @Title: sendGeneralCall
+	* @Description: 发送总召唤
+	 */
+	public void sendGeneralCall() {
+		synchronized (senGeneralCallStatus) {
+			if (generalCallTThread != null && generalCallTThread.getState() == Thread.State.TERMINATED) {
+				senGeneralCallStatus = true;
+				generalCallTThread.start();
+			} else if (generalCallTThread == null) {
+				senGeneralCallStatus = true;
+				generalCallTThread = new Thread(new Runnable() {
+					@Override
+					public void run() {
+						while (sendTestStatus) {
+							try {
+								LOGGER.info("发送总召唤指令");
+								ctx.channel().writeAndFlush(BasicInstruction104.getGeneralCallRuleDetail104());
+								Thread.sleep(1000 * 60 * 3);
+							} catch (Exception e) {
+								e.printStackTrace();
+							}
+						}
+					}
+				});
+				generalCallTThread.start();
+			}
+		}
+	}
+
+}

+ 90 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/enums/QualifiersEnum.java

@@ -0,0 +1,90 @@
+package com.ydl.iec.iec104.enums;
+
+import lombok.Getter;
+
+/**
+ * 
+* @ClassName: 限定词  
+* @author YDL
+ */
+public enum QualifiersEnum {
+
+	/**
+	 *  总召唤限定词
+	 */
+	generalCallQualifiers(TypeIdentifierEnum.generalCall, 0x20),
+	
+	/**
+	 * 总召唤限定词 支持 老版的分组
+	 */
+	generalCallGroupingQualifiers(TypeIdentifierEnum.generalCall, 0x14),
+	/**
+	 * 复位进程限定词
+	 */
+	resetPprocessQualifiers(TypeIdentifierEnum.resetPprocess, 0x01),
+	/**
+	 *  初始化原因 当地电源合上
+	 */
+	localCloseUpQualifiers(TypeIdentifierEnum.initEnd, 0x00),
+	/**
+	 * 初始化原因 当地手动复位
+	 */
+	localMmanualResetQualifiers(TypeIdentifierEnum.initEnd, 0x01),
+	/**
+	 * 远方复位 
+	 */
+	distanceResetQualifiers(TypeIdentifierEnum.initEnd, 0x02),
+	/**
+	 * 品质描述词  遥测
+	 */
+	qualityQualifiers(null, 0x00),
+	/**
+	 * 设置命令限定词  选择预置参数 1000 0000
+	 */
+	prefabParameterQualifiers(null, 0x40),
+	/**
+	 * 设置命令限定词  执行激活参数
+	 */
+	activationParameterQualifiers(null, 0x0F);
+
+	@Getter
+	private byte value;
+	@Getter
+	private TypeIdentifierEnum typeIdentifier;
+
+	QualifiersEnum(TypeIdentifierEnum typeIdentifier, int value) {
+		this.value = (byte) value;
+		this.typeIdentifier = typeIdentifier;
+	}
+
+
+	/**
+	 * 	根据传输类型和 限定词的关系返回 限定词的类型
+	 * @param typeIdentifier
+	 * @param value
+	 * @return
+	 */
+	public static QualifiersEnum getQualifiersEnum(TypeIdentifierEnum typeIdentifier, byte value) {
+		for (QualifiersEnum type : QualifiersEnum.values()) {
+			if (type.getValue() == value && type.getTypeIdentifier() == typeIdentifier) {
+				return type;
+			}
+		}
+		// 品质描述词和设置参数 限定词对应多个值 所以需要做特殊处理
+		QualifiersEnum qualifiersEnum = null;
+		if ((TypeIdentifierEnum.normalizedTelemetry.equals(typeIdentifier) 
+				|| TypeIdentifierEnum.scaledTelemetry.equals(typeIdentifier)
+				|| TypeIdentifierEnum.shortFloatingPointTelemetry.equals(typeIdentifier)) 
+				&& qualityQualifiers.getValue() == value) {
+			qualifiersEnum = qualityQualifiers;
+		}
+		if ((TypeIdentifierEnum.readOneParameter.equals(typeIdentifier) 
+				|| TypeIdentifierEnum.readMultipleParameter.equals(typeIdentifier)
+				|| TypeIdentifierEnum.prefabActivationOneParameter.equals(typeIdentifier)
+				|| TypeIdentifierEnum.prefabActivationMultipleParameter.equals(typeIdentifier)) 
+				&& qualityQualifiers.getValue() == value) {
+			qualifiersEnum = qualityQualifiers;
+		}
+		return qualifiersEnum;
+	}
+}

+ 105 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/enums/TypeIdentifierEnum.java

@@ -0,0 +1,105 @@
+package com.ydl.iec.iec104.enums;
+
+import lombok.Getter;
+
+/**
+ * 
+* @ClassName: TypeIdentifierEnum   类型标识
+* @author YDL
+* @date 2020年5月13日
+ */
+public enum TypeIdentifierEnum {
+
+	/**
+	 * 单点摇信
+	 */
+	onePointTeleindication(0x01, 1),
+	/**
+	 * 双点摇信
+	 */
+	twoPointTeleindication(0x03, 1),
+	/**
+	 * 测量值 归一化值 遥测
+	 */
+	normalizedTelemetry(0x09,2),
+	/**
+	 * 测量值  标度化值 遥测
+	 */
+	scaledTelemetry(0x0B, 2),
+	/**
+	 * 测量值 短浮点数 遥测   Short floating point
+	 */
+	shortFloatingPointTelemetry(0x0D, 2),
+	/**
+	 * 摇信带时标 单点
+	 */
+	onePointTimeTeleindication(0x1E, 1),
+	/**
+	 * 摇信带时标 双点
+	 */
+	twoPointTimeTeleindication(0x1F, 1),
+	/**
+	 * 单命令 遥控
+	 */
+	onePointTelecontrol(0x2D, 1),
+	/**
+	 * 双命令遥控
+	 */
+	twoPointTelecontrol(0x2E, 1),
+	/**
+	 * 读单个参数
+	 */
+	readOneParameter(0x66, 4),
+	/**
+	 * 读多个参数
+	 */
+	readMultipleParameter(0x84, 4),
+	/**
+	 * 预置单个参数命令
+	 */
+	prefabActivationOneParameter(0x30, 4),
+	/**
+	 * 预置多个个参数
+	 */
+	prefabActivationMultipleParameter(0x88, 4),
+	/**
+	 * 初始化结束
+	 */
+	initEnd(0x46, 0),
+	/**
+	 * 召唤命令
+	 */
+	generalCall(0x64, 0),
+	/**
+	 * 时钟同步
+	 */
+	timeSynchronization(0x67, 0),
+	/**
+	 * 复位进程
+	 */
+	resetPprocess(0x69, 0);
+
+	@Getter
+	private byte value;
+	@Getter
+	private int messageLength;
+
+	TypeIdentifierEnum(int value, int messageLength) {
+		this.value = (byte) value;
+		this.messageLength = messageLength;
+	}
+	public static TypeIdentifierEnum getTypeIdentifierEnum(byte value) {
+		for (TypeIdentifierEnum type : TypeIdentifierEnum.values()) {
+			if (type.getValue() == value) {
+				return type;
+			}
+		}
+		return null;
+	}
+
+
+	public static boolean isTelemetry(TypeIdentifierEnum typeIdentifierEnum) {
+		return TypeIdentifierEnum.normalizedTelemetry == typeIdentifierEnum
+				|| scaledTelemetry.shortFloatingPointTelemetry == typeIdentifierEnum;
+	}
+}

+ 42 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/enums/UControlEnum.java

@@ -0,0 +1,42 @@
+package com.ydl.iec.iec104.enums;
+
+import lombok.Getter;
+
+/**
+ * U帧 基本指令
+ *
+ */
+public enum UControlEnum {
+	/**
+	 * 测试命令
+	 */
+	TESTFR(0x43000000),
+	/**
+	 * 测试确认指令
+	 */
+	TESTFR_YES(0x83000000),
+	/**
+	 * 停止指令
+	 */
+	STOPDT(0x13000000),
+	/**
+	 * 停止确认
+	 */
+	STOPDT_YES(0x23000000),
+	/**
+	 * 启动命令
+	 */
+	STARTDT(0x07000000),
+	/**
+	 * 启动确认命令
+	 */
+	STARTDT_YES(0x0B000000);
+
+	@Getter
+	private  int value;
+
+	UControlEnum(int value) {
+		this.value = value;
+	}
+
+}

+ 148 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/message/MessageDetail.java

@@ -0,0 +1,148 @@
+package com.ydl.iec.iec104.message;
+
+import com.ydl.iec.util.Iec104Util;
+import com.ydl.iec.iec104.enums.QualifiersEnum;
+import com.ydl.iec.iec104.enums.TypeIdentifierEnum;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ *  一条报文对应的消息体
+ * @author YDL
+ */
+@Data
+public class MessageDetail {
+	
+	/**
+	 * 启动字符 固定 一个字节
+	 */
+	private byte start = 0x68;
+	
+	/**
+	 * APUU 长度1个字节
+	 */
+	private int apuuLength = 0;
+	
+	/**
+	 * 控制域 四个字节
+	 */
+	private byte[] control;
+	
+	
+	/**
+	 * 类型标识 1字节
+	 */
+	private TypeIdentifierEnum typeIdentifier;
+	
+	
+	/**
+	 * 可变结构限定词 1个字节 
+	 * true SQ = 0   true 数目number 是 信息对象的数目
+	 * false SQ = 1 单个对象的信息元素或者信息元素的集合的数目
+	 */
+	
+	private boolean  isContinuous;
+
+	/**
+	 * 消息长度
+	 */
+	private int measgLength;
+	/**
+	 * 传输原因 两个字节
+	 */
+	private  short transferReason;
+	
+	/**
+	 *  终端地址 也就是应用服务数据单元公共地址
+	 */
+	private short terminalAddress;
+	
+	/**
+	 * 消息地址 字节
+	 */
+	private int messageAddress;
+	
+	/**
+	 * 消息结构
+	 */
+	private List<MessageInfo> messages;
+	
+	
+	/**
+	 * 判断是否有消息元素
+	 */
+	private boolean isMessage;
+	/**
+	 * 判断是否有限定词
+	 */
+	private boolean isQualifiers;
+	/**
+	 * 判断是否有时标
+	 */
+	private boolean isTimeScaleExit;
+	
+	private QualifiersEnum qualifiersType;
+	/**
+	 * 
+	 * 时标
+	 */
+	private  Date timeScale;
+
+	/**
+	 * 十六进制 字符串
+	 */
+	private String hexString;
+
+	public MessageDetail() {
+	}
+
+	/**
+	 * 
+	 * @param control 控制域
+	 * @param typeIdentifierEnum 类型标识
+	 * @param sq  0 地址不连续  1 地址连续
+	 * @param isTest 传输原因  0 未试验 1 试验
+	 * @param isPn 肯定确认 和否定确认
+	 * @param transferReason 传输原因 后六个比特位
+	 * @param terminalAddress 服务地址
+	 * @param messageAddress 消息地址
+	 * @param messages 消息列表
+	 * @param timeScale 时间
+	 * @param qualifiers 限定词
+	 * @return
+	 */
+	public MessageDetail(byte[] control, TypeIdentifierEnum typeIdentifierEnum, boolean sq,
+						 boolean isTest, boolean isPn, short transferReason, short terminalAddress, int messageAddress,
+						 List<MessageInfo> messages, Date timeScale, QualifiersEnum qualifiers) {
+		this.control = control;
+		this.typeIdentifier = typeIdentifierEnum;
+		this.isContinuous = sq;
+		this.measgLength = messages.size();
+		this.transferReason = Iec104Util.getTransferReasonShort(isTest, isPn, transferReason);
+		this.messages = messages;
+		this.terminalAddress = terminalAddress;
+		this.timeScale = timeScale;
+		if (isContinuous) {
+			// 只有连续地址才会在次设置地址,
+			this.messageAddress = messageAddress;
+		}
+		if (timeScale != null) {
+			this.isTimeScaleExit = true;
+		}
+		this.qualifiersType = qualifiers;
+	}
+	
+	
+	/**
+	 *  U 帧或者S帧
+	 * @param control 控制域
+	 */
+	public MessageDetail(byte[] control) {
+		this.control = control;
+		this.messages = new ArrayList<>();
+	}
+	
+}

+ 38 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/message/MessageInfo.java

@@ -0,0 +1,38 @@
+package com.ydl.iec.iec104.message;
+
+import com.ydl.iec.iec104.enums.QualifiersEnum;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 报文中 的消息部分
+ */
+@Data
+public class MessageInfo {
+	/**
+	 * 消息地址 字节
+	 */
+	private int messageAddress;
+	
+	/**
+	 * 信息元素集合 1 2 4 个字节
+	 */
+	private byte[] messageInfos;
+
+	/**
+	 * 限定词
+	 */
+	private QualifiersEnum qualifiersType;
+//	/**
+//	 *
+//	 * 时标
+//	 */
+//	private  Date timeScale;
+
+	/**
+	 * 消息详情
+	 */
+	private int messageInfoLength;
+
+}

+ 32 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/Iec104Master.java

@@ -0,0 +1,32 @@
+package com.ydl.iec.iec104.server;
+
+import com.ydl.iec.iec104.config.Iec104Config;
+import com.ydl.iec.iec104.server.handler.DataHandler;
+
+/**
+ * 主站抽象类
+ */
+public interface Iec104Master {
+
+	/**
+	 * 服务启动方法
+	 * @throws Exception
+	 */
+	void run() throws Exception;
+	
+	/**
+	 * 
+	* @Title: setDataHandler
+	* @Description: 设置数据处理类
+	* @param dataHandler
+	 */
+	Iec104Master setDataHandler(DataHandler dataHandler);
+
+	/**
+	 * 设置配置文件
+	 * @param iec104Confiig
+	 * @return
+	 */
+	Iec104Master setConfig(Iec104Config iec104Confiig);
+
+}

+ 26 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/Iec104MasterFactory.java

@@ -0,0 +1,26 @@
+package com.ydl.iec.iec104.server;
+
+import com.ydl.iec.iec104.server.master.Iec104TcpClientMaster;
+
+/**
+ * 主站 工厂类
+ * @ClassName:  Iec104MasterFactory   
+ * @Description: IEC104规约主站
+ * @author: YDL
+ * @date:   2020年5月19日 上午10:22:59
+ */
+public class Iec104MasterFactory {
+
+ 
+
+	/**
+	* @Title: createTcpClientMaster
+	* @Description: 创建一个TCM客户端的104主站
+	* @param host 从机地址
+	* @param port 端口
+	* @return
+	 */
+	public static  Iec104Master createTcpClientMaster(String host, int port) {
+		return new Iec104TcpClientMaster(host, port);
+	}
+}

+ 34 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/Iec104Slave.java

@@ -0,0 +1,34 @@
+package com.ydl.iec.iec104.server;
+
+import com.ydl.iec.iec104.config.Iec104Config;
+import com.ydl.iec.iec104.server.handler.DataHandler;
+
+/**
+ * 从站抽象类
+ */
+public interface Iec104Slave {
+	/**
+	 * 
+	* @Title: run
+	* @Description: 启动主机
+	* @throws Exception
+	 */
+	void run() throws Exception;
+	
+	
+	/**
+	 * 
+	* @Title: setDataHandler
+	* @Description: 设置数据处理类
+	* @param dataHandler
+	 */
+	Iec104Slave setDataHandler(DataHandler dataHandler);
+
+
+	/**
+	 * 设置配置文件
+	 * @param iec104Config
+	 * @return
+	 */
+	Iec104Slave setConfig(Iec104Config iec104Config);
+}

+ 24 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/Iec104SlaveFactory.java

@@ -0,0 +1,24 @@
+package com.ydl.iec.iec104.server;
+
+import com.ydl.iec.iec104.server.slave.Iec104TcpServerSlave;
+
+/**
+ * 
+ * @ClassName:  Iec104SlaveFactory   
+ * @Description:  104从机工厂
+ * @author: YDL
+ * @date:   2020年5月19日 上午10:41:39
+ */
+public class Iec104SlaveFactory {
+
+	/**
+	 * 
+	* @Title: createTcpServerSlave  
+	* @Description:  生产一个 iec104 协议TCP传输方式服务端做从机服务
+	* @param  port 端口 从机端口
+	* @return Iec104Slave
+	 */
+	public static  Iec104Slave createTcpServerSlave(int port) {
+		return new Iec104TcpServerSlave(port);
+	}
+}

+ 21 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/BytesEncoder.java

@@ -0,0 +1,21 @@
+package com.ydl.iec.iec104.server.handler;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+
+/**
+ * 数组编码器
+ * @author Admin
+ *
+ */
+public class BytesEncoder extends MessageToByteEncoder<byte[]> {
+
+	
+	@Override
+	protected void encode(ChannelHandlerContext ctx, byte[] msg, ByteBuf out) throws Exception {
+		out.writeBytes(msg);
+	}
+
+}

+ 14 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/ChannelHandler.java

@@ -0,0 +1,14 @@
+package com.ydl.iec.iec104.server.handler;
+
+import com.ydl.iec.iec104.message.MessageDetail;
+
+/**
+ * 
+ * @ClassName:  ChannelHandler   
+ * @Description: 处理数据
+ * @author: YDL
+ * @date:   2020年5月19日 上午11:41:58
+ */
+public interface ChannelHandler {
+	void writeAndFlush(MessageDetail ruleDetail104);
+}

+ 26 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/ChannelHandlerImpl.java

@@ -0,0 +1,26 @@
+package com.ydl.iec.iec104.server.handler;
+
+import com.ydl.iec.iec104.message.MessageDetail;
+import io.netty.channel.ChannelHandlerContext;
+
+/**
+ * 
+ * @ClassName:  ChannelHandlerImpl   
+ * @Description: 实现一个自定义发现消息的类
+ * @author: YDL
+ * @date:   2020年5月19日 上午11:47:16
+ */
+public class ChannelHandlerImpl implements  ChannelHandler {
+	
+	private ChannelHandlerContext ctx;
+	
+	public ChannelHandlerImpl(ChannelHandlerContext ctx) {
+		this.ctx = ctx;
+	}
+
+	@Override
+	public void writeAndFlush(MessageDetail ruleDetail104) {
+		ctx.channel().writeAndFlush(ruleDetail104);
+	}
+
+}

+ 40 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/Check104Handler.java

@@ -0,0 +1,40 @@
+package com.ydl.iec.iec104.server.handler;
+
+import com.ydl.iec.iec104.common.Iec104Constant;
+import com.ydl.iec.util.ByteUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.ReferenceCountUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+* @ClassName: Check104Handler  
+* @Description: 检查104报文 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class Check104Handler extends ChannelInboundHandlerAdapter {
+	
+	private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);
+	
+	/**
+	 * 拦截系统消息
+	 */
+	@Override
+	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+		ByteBuf result = (ByteBuf) msg;
+		byte[] bytes = new byte[result.readableBytes()];
+		result.readBytes(bytes);
+		LOGGER.info("接收到的报文: " + ByteUtil.byteArrayToHexString(bytes));
+		if (bytes.length < Iec104Constant.APCI_LENGTH || bytes[0] != Iec104Constant.HEAD_DATA) {
+			LOGGER.error("报文无效");
+			ReferenceCountUtil.release(result);
+		} else {
+			result.writeBytes(bytes);
+			ctx.fireChannelRead(msg);
+		}
+	}
+}

+ 34 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/DataDecoder.java

@@ -0,0 +1,34 @@
+package com.ydl.iec.iec104.server.handler;
+
+import com.ydl.iec.iec104.core.Iec104ThreadLocal;
+import com.ydl.iec.util.Iec104Util;
+import com.ydl.iec.iec104.core.Decoder104;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.util.ByteUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+
+/**
+ * 解码器
+ * @author Admin
+ *
+ */
+public class DataDecoder extends ByteToMessageDecoder {
+	
+	@Override
+	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+		byte[] data = new byte[in.readableBytes()];
+		in.readBytes(data);
+		short send = Iec104Util.getSend(ByteUtil.getByte(data, 2, 4));
+		Iec104ThreadLocal.getControlPool().setAccept(send);
+		MessageDetail ruleDetail104 = Decoder104.encoder(data);
+		out.add(ruleDetail104);
+	}
+}

+ 46 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/DataEncoder.java

@@ -0,0 +1,46 @@
+package com.ydl.iec.iec104.server.handler;
+
+
+import com.ydl.iec.iec104.core.Iec104ThreadLocal;
+import com.ydl.iec.util.Iec104Util;
+import com.ydl.iec.iec104.core.Encoder104;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.util.ByteUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 编码器
+ * @author Admin
+ *
+ */
+@Slf4j
+public class DataEncoder extends MessageToByteEncoder<MessageDetail> {
+
+	
+	@Override
+	protected void encode(ChannelHandlerContext ctx, MessageDetail msg, ByteBuf out) throws Exception {
+		try {
+			byte[] bytes = Encoder104.encoder(msg);
+			short accept = Iec104ThreadLocal.getControlPool().getAccept();
+			short send = Iec104ThreadLocal.getControlPool().getSend();
+			short terminalAddress = Iec104ThreadLocal.getIec104Conig().getTerminnalAddress();
+			// 替换终端地址 发送序号和接收序号
+			byte[] terminalAddressBytes = Iec104Util.getTerminalAddressByte(terminalAddress);
+			byte[] icontrol = Iec104Util.getIcontrol(accept, send);
+			for (int i = 0; i < icontrol.length; i++) {
+				bytes[i + 2] = icontrol[i];
+			}
+			bytes[10] = terminalAddressBytes[0];
+			bytes[11] = terminalAddressBytes[1];
+			log.info(ByteUtil.byteArrayToHexString(bytes));
+			out.writeBytes(bytes);
+		} catch (Exception e) {
+			log.error("", e);
+		}
+
+	}
+
+}

+ 32 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/DataHandler.java

@@ -0,0 +1,32 @@
+package com.ydl.iec.iec104.server.handler;
+
+import com.ydl.iec.iec104.message.MessageDetail;
+
+/**
+ * 
+ * @ClassName:  DataHandler   
+ * @Description:  数据处理
+ * @author: YDL
+ * @date:   2020年5月19日 上午11:27:04
+ */
+public interface DataHandler {
+
+	/**
+	 * 
+	* @Title: handlerAdded
+	* @Description: 建立连接
+	* @param ctx
+	* @throws Exception
+	 */
+	void handlerAdded(ChannelHandler ctx) throws Exception;
+	
+	/**
+	 * 
+	* @Title: channelRead0
+	* @Description: 收到消息
+	* @param ctx
+	* @param ruleDetail104
+	* @throws Exception
+	 */
+	void channelRead(ChannelHandler ctx, MessageDetail ruleDetail104) throws Exception;
+}

+ 62 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/SysSframeHandler.java

@@ -0,0 +1,62 @@
+package com.ydl.iec.iec104.server.handler;
+
+import com.ydl.iec.iec104.common.Iec104Constant;
+import com.ydl.iec.util.Iec104Util;
+import com.ydl.iec.util.ByteUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.ReferenceCountUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+* @ClassName: SysSframeInboundHandler  
+* @Description: 处理S帧的问题 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class SysSframeHandler extends ChannelInboundHandlerAdapter {
+
+	private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);
+	
+	/**
+	 * 拦截系统消息 
+	 */
+	@Override
+	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+		ByteBuf result = (ByteBuf) msg;
+		byte[] bytes = new byte[result.readableBytes()];
+		result.readBytes(bytes);
+		if (isSysInstruction(bytes)) {
+			LOGGER.info("收到S帧" + Iec104Util.getAccept(ByteUtil.getByte(bytes, 2, 4)));
+			ReferenceCountUtil.release(result);
+			return;
+		} 
+		result.writeBytes(bytes);
+//		LOGGER.info("普通指令");
+		ctx.fireChannelRead(result);
+	}
+
+	/**
+	 * 
+	* @Title: isSysInstruction  
+	* @Description: TODO  判断是否是 系统报文
+	* @param @param bytes
+	* @param @return 
+	* @return boolean   
+	* @throws
+	 */
+	private boolean isSysInstruction(byte[] bytes) {
+		if (bytes.length != Iec104Constant.APCI_LENGTH) {
+			return false;
+		}
+		if (bytes[Iec104Constant.ACCEPT_LOW_INDEX] == 1 && bytes[Iec104Constant.ACCEPT_HIGH_INDEX] == 0) {
+			// 判断S帧的方法
+			return true;
+		}
+		// U帧只有6字节
+		return false;
+	}
+}

+ 52 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/handler/Unpack104Handler.java

@@ -0,0 +1,52 @@
+package com.ydl.iec.iec104.server.handler;
+
+import com.ydl.iec.iec104.common.Iec104Constant;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+
+import java.util.List;
+
+/**
+ * 
+* @ClassName: Unpack104Util  
+* @Description: 解决TCP 拆包和沾包的问题 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class Unpack104Handler extends ByteToMessageDecoder {
+
+	@Override
+	protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
+        // 记录包头开始的index  
+        int beginReader;  
+        int newDataLength = 0;
+        while (true) {  
+            // 获取包头开始的index  
+            beginReader = buffer.readerIndex();  
+            // 记录一个标志用于重置
+            buffer.markReaderIndex();  
+            // 读到了协议的开始标志,结束while循环  
+            if (buffer.readByte() == Iec104Constant.HEAD_DATA) {
+            	// 标记当前包为新包
+            	//读取包长度
+            	byte newDataLengthByte = buffer.readByte();
+                newDataLength = newDataLengthByte & 0xFF;
+                break;
+            }
+            continue;
+        }
+
+        if (buffer.readableBytes() < newDataLength) {
+            buffer.readerIndex(beginReader);
+            return;
+        }
+
+        newDataLength = newDataLength +  2;
+        //恢复指针
+        buffer.readerIndex(beginReader);
+        ByteBuf data = buffer.readBytes(newDataLength);
+        out.add(data); 
+	}
+
+}

+ 60 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/master/Iec104ClientInitializer.java

@@ -0,0 +1,60 @@
+package com.ydl.iec.iec104.server.master;
+
+import com.ydl.iec.iec104.config.DefaultIec104Config;
+import com.ydl.iec.iec104.config.Iec104Config;
+import com.ydl.iec.iec104.core.Iec104ThreadLocal;
+import com.ydl.iec.iec104.server.handler.*;
+import com.ydl.iec.iec104.server.master.handler.Iec104ClientHandler;
+import com.ydl.iec.iec104.server.master.handler.SysUframeClientHandler;
+import com.ydl.iec.iec104.server.slave.Iec104ServerInitializer;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+/**
+ * 
+* @ClassName: Iec104ServerInitializer  
+* @Description: 104协议 处理链 
+* @author YDL 
+* @date 2020年5月13日
+ */
+@Setter
+@Accessors(chain = true)
+public class Iec104ClientInitializer extends ChannelInitializer<SocketChannel> {
+
+
+	private DataHandler dataHandler;
+
+	private Iec104Config iec104Config;
+	
+	/**
+	 * 初始化处理链
+	 */
+	@Override
+	public void initChannel(SocketChannel ch) throws Exception {
+		if (iec104Config == null) {
+			Iec104ThreadLocal.setIec104Config(iec104Config);
+		} else {
+			Iec104ThreadLocal.setIec104Config(new DefaultIec104Config());
+		}
+		ChannelPipeline pipeline = ch.pipeline();
+		// 沾包拆包工具
+		pipeline.addLast("unpack", new Unpack104Handler());
+		// 数据检查工具
+		pipeline.addLast("check", new Check104Handler());
+//		/拦截 U帧处理器 
+		pipeline.addLast("uframe", new SysUframeClientHandler());
+		//拦截 S帧处理器 
+		pipeline.addLast("sframe", new SysSframeHandler());
+		//编码器
+		pipeline.addLast("byteencoder", new BytesEncoder());
+		//编码器
+		pipeline.addLast("encoder", new DataEncoder());
+
+		//解码器
+		pipeline.addLast("decoder", new DataDecoder());
+		pipeline.addLast("handler", new Iec104ClientHandler(dataHandler));
+	}
+}

+ 57 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/master/Iec104TcpClientMaster.java

@@ -0,0 +1,57 @@
+package com.ydl.iec.iec104.server.master;
+
+
+import com.ydl.iec.iec104.config.Iec104Config;
+import com.ydl.iec.iec104.server.Iec104Master;
+import com.ydl.iec.iec104.server.handler.DataHandler;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+
+/**
+ * 
+ * @ClassName:  Iec104TcpClientMaster   
+ * @Description: 104 TCP 客户端 主机
+ * @author: YDL
+ * @date:   2020年5月19日 上午10:44:35
+ */
+public class Iec104TcpClientMaster implements Iec104Master {
+
+	private int port;
+	private String host;
+	private DataHandler dataHandler;
+	private Iec104Config iec104Config;
+
+	public Iec104TcpClientMaster(String host, int port) {
+		this.port = port;
+		this.host = host;
+	}
+
+	@Override
+	public void run() throws Exception {
+		EventLoopGroup bossGroup = new NioEventLoopGroup();
+	    Bootstrap bs = new Bootstrap();
+	    bs.group(bossGroup)
+	      .channel(NioSocketChannel.class)
+	      .option(ChannelOption.SO_KEEPALIVE, true)
+	      .handler(new Iec104ClientInitializer().setDataHandler(dataHandler).setIec104Config(iec104Config));
+	    // 客户端开启
+	    Channel channel  = bs.connect(host, port).sync().channel();
+	    channel.closeFuture().sync();
+	}
+
+	@Override
+	public Iec104Master setDataHandler(DataHandler dataHandler) {
+		this.dataHandler = dataHandler;
+		return this;
+	}
+
+	@Override
+	public Iec104Master setConfig(Iec104Config iec104Config) {
+		this.iec104Config = iec104Config;
+		return this;
+	}
+}

+ 65 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/master/handler/Iec104ClientHandler.java

@@ -0,0 +1,65 @@
+package com.ydl.iec.iec104.server.master.handler;
+
+
+import com.ydl.iec.iec104.core.CachedThreadPool;
+import com.ydl.iec.iec104.core.ControlManageUtil;
+import com.ydl.iec.iec104.core.Iec104ThreadLocal;
+import com.ydl.iec.iec104.core.ScheduledTaskPool;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.server.handler.ChannelHandlerImpl;
+import com.ydl.iec.iec104.server.handler.DataHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+import java.io.IOException;
+
+public class Iec104ClientHandler extends SimpleChannelInboundHandler<MessageDetail> {
+
+	private DataHandler dataHandler;
+
+	public Iec104ClientHandler(DataHandler dataHandler) {
+		this.dataHandler = dataHandler;
+	}
+	
+	
+	@Override
+	public void channelActive(ChannelHandlerContext ctx) throws Exception {
+		// 启动成功后一直发启动链路命令
+		Iec104ThreadLocal.setScheduledTaskPool(new ScheduledTaskPool(ctx));
+		Iec104ThreadLocal.getScheduledTaskPool().sendStatrFrame();
+		Iec104ThreadLocal.setControlPool(new ControlManageUtil(ctx).setFrameAmountMax(Iec104ThreadLocal.getIec104Conig().getFrameAmountMax()));
+		Iec104ThreadLocal.getControlPool().startSendFrameTask();
+
+		if (dataHandler != null) {
+			CachedThreadPool.getCachedThreadPool().execute(new Runnable() {
+				@Override
+				public void run() {
+					try {
+						dataHandler.handlerAdded(new ChannelHandlerImpl(ctx));
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+			});
+        }
+	}
+	
+	@Override
+	public void channelRead0(ChannelHandlerContext ctx, MessageDetail ruleDetail104) throws IOException {
+		if (dataHandler != null) {
+			CachedThreadPool.getCachedThreadPool().execute(new Runnable() {
+    			@Override
+    			public void run() {
+    				try {
+						dataHandler.channelRead(new ChannelHandlerImpl(ctx), ruleDetail104);
+					} catch (Exception e) {
+						// TODO Auto-generated catch block
+						e.printStackTrace();
+					}
+    			}
+    		});
+    	}
+	}
+
+
+}

+ 83 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/master/handler/SysUframeClientHandler.java

@@ -0,0 +1,83 @@
+package com.ydl.iec.iec104.server.master.handler;
+
+import com.ydl.iec.iec104.config.Iec104Config;
+import com.ydl.iec.iec104.core.Iec104ThreadLocal;
+import com.ydl.iec.util.Iec104Util;
+import com.ydl.iec.iec104.enums.UControlEnum;
+import com.ydl.iec.util.ByteUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+* @ClassName: SysUframeInboundHandler  
+* @Description: 处理U帧的报文 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class SysUframeClientHandler extends ChannelInboundHandlerAdapter {
+
+	private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);
+	
+	/**
+	 * 拦截系统消息 
+	 */
+	@Override
+	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+		ByteBuf result = (ByteBuf) msg;
+		byte[] bytes = new byte[result.readableBytes()];
+		result.readBytes(bytes);
+		if (isSysInstruction(bytes)) {
+			UControlEnum uControlEnum = Iec104Util.getUcontrol(ByteUtil.getByte(bytes, 2, 4));
+			if (uControlEnum != null) {
+				uInstructionHandler(ctx, result, uControlEnum);
+				return;
+			}
+		} 
+		result.writeBytes(bytes);
+		ctx.fireChannelRead(result);
+	}
+
+	/**
+	 * 
+	* @Title: isSysInstruction  
+	* @Description: TODO  判断是否是 系统报文
+	* @param @param bytes
+	* @param @return 
+	* @return boolean   
+	* @throws
+	 */
+	private boolean isSysInstruction(byte[] bytes) {
+		// U帧只有6字节
+		return bytes.length == 6;
+	}
+	
+	/**
+	 * 
+	* @Title: uInstructionHandler  
+	* @Description: 处理U帧 
+	* @param ctx
+	* @param result
+	* @param uControlEnum 
+	* @return void   
+	* @throws
+	 */
+	private void uInstructionHandler(ChannelHandlerContext ctx, ByteBuf result, UControlEnum uControlEnum) {
+		result.readBytes(new byte[result.readableBytes()]);
+		if (uControlEnum == UControlEnum.TESTFR_YES) {
+			LOGGER.info("收到测试确认指令");
+			Iec104ThreadLocal.getScheduledTaskPool().sendGeneralCall();
+		} else if (uControlEnum == UControlEnum.STOPDT_YES) {
+			LOGGER.info("收到停止确认指令");
+		} else if (uControlEnum == UControlEnum.STARTDT_YES) {
+			LOGGER.info("收到启动指令确认指令");
+			Iec104ThreadLocal.getScheduledTaskPool().stopSendStatrFrame();
+			Iec104ThreadLocal.getScheduledTaskPool().sendTestFrame();
+		} else {
+			LOGGER.error("U报文无效");
+		}
+	}
+}

+ 62 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/slave/Iec104ServerInitializer.java

@@ -0,0 +1,62 @@
+package com.ydl.iec.iec104.server.slave;
+
+import com.ydl.iec.iec104.config.DefaultIec104Config;
+import com.ydl.iec.iec104.config.Iec104Config;
+import com.ydl.iec.iec104.core.Iec104ThreadLocal;
+import com.ydl.iec.iec104.server.Iec104Slave;
+import com.ydl.iec.iec104.server.handler.*;
+import com.ydl.iec.iec104.server.slave.handler.Iec104TcpSlaveHandler;
+import com.ydl.iec.iec104.server.slave.handler.SysUframeServerHandler;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+/**
+ * 
+* @ClassName: Iec104ServerInitializer  
+* @Description: 104协议 处理链 
+* @author YDL 
+* @date 2020年5月13日
+ */
+@Setter
+@Accessors(chain = true)
+public class Iec104ServerInitializer extends ChannelInitializer<SocketChannel> {
+
+	private DataHandler dataHandler;
+
+
+	private Iec104Config iec104Config;
+
+	/**
+	 * 初始化处理链
+	 */
+	@Override
+	public void initChannel(SocketChannel ch) {
+		if (iec104Config == null) {
+			Iec104ThreadLocal.setIec104Config(iec104Config);
+		} else {
+			Iec104ThreadLocal.setIec104Config(new DefaultIec104Config());
+		}
+		ChannelPipeline pipeline = ch.pipeline();
+		// 沾包拆包工具
+		pipeline.addLast("unpack", new Unpack104Handler());
+		// 数据检查工具
+		pipeline.addLast("check", new Check104Handler());
+		// byte[] 编码器
+		pipeline.addLast("bytesEncoder", new BytesEncoder());
+		
+		//编码器 将对象编码成 字节码
+		pipeline.addLast("encoder", new DataEncoder());
+		
+		//拦截 U帧处理器 
+		pipeline.addLast("uframe", new SysUframeServerHandler());
+		//拦截 S帧处理器 
+		pipeline.addLast("sframe", new SysSframeHandler());
+		//解码器
+		pipeline.addLast("decoder", new DataDecoder());
+		// 具体的处理器
+		pipeline.addLast("handler", new Iec104TcpSlaveHandler(dataHandler));
+	}
+}

+ 85 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/slave/Iec104TcpServerSlave.java

@@ -0,0 +1,85 @@
+package com.ydl.iec.iec104.server.slave;
+
+import com.ydl.iec.iec104.config.DefaultIec104Config;
+import com.ydl.iec.iec104.config.Iec104Config;
+import com.ydl.iec.iec104.core.Iec104ThreadLocal;
+import com.ydl.iec.iec104.server.Iec104Slave;
+import com.ydl.iec.iec104.server.handler.DataHandler;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+
+
+/**
+ * iec 104 TCP 协议 服务端做从机
+ * @author Admin
+ *
+ */
+public class Iec104TcpServerSlave implements Iec104Slave {
+	
+    private int port;
+    private Iec104Config iec104Config;
+
+
+    private DataHandler dataHandler;
+    
+    public Iec104TcpServerSlave(int port) {
+        this.port = port;
+    }
+    
+    /**
+     * 启动 从机
+     */
+    @Override
+    public void run() throws Exception {
+
+        Iec104ThreadLocal.setIec104Config(iec104Config);
+    	//1 EventLoopGroup是一个线程组,它包含了一组nio线程,专门用于网络事件的处理,实际上他们就是Reactor线程组  
+        EventLoopGroup bossGroup = new NioEventLoopGroup(); 
+//       这 里创建2个的原因是一个用于服务端接收客户的连接,另一个用于SockentChannel的网络读写。
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+        try {
+        	// 1
+            ServerBootstrap b = new ServerBootstrap(); 
+            b.group(bossGroup, workerGroup)
+            // (3) NIO
+            .channel(NioServerSocketChannel.class)
+             //(4)
+            .childHandler(getIec104ServerInitializer())
+             //(5)  请求的队列的最大长度
+            .option(ChannelOption.SO_BACKLOG, 128)          
+             // (6) 保持长连接
+            .childOption(ChannelOption.SO_KEEPALIVE, true);
+            //7绑定端口,开始接收进来的连接
+            ChannelFuture f = b.bind(port).sync(); 
+            // 等待服务器  socket 关闭 。
+            // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
+            f.channel().closeFuture().sync();
+        } finally {
+            workerGroup.shutdownGracefully();
+            bossGroup.shutdownGracefully();
+        }
+    }
+    
+    private Iec104ServerInitializer getIec104ServerInitializer() {
+    	Iec104ServerInitializer iec104ServerInitializer = new Iec104ServerInitializer();
+    	iec104ServerInitializer.setDataHandler(dataHandler).setIec104Config(iec104Config);
+    	return iec104ServerInitializer;
+    }
+
+
+	@Override
+	public Iec104Slave setDataHandler(DataHandler dataHandler) {
+		this.dataHandler = dataHandler;
+		return this;
+	}
+
+    @Override
+    public Iec104Slave setConfig(Iec104Config iec104Config) {
+        this.iec104Config = iec104Config;
+        return this;
+    }
+}

+ 104 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/slave/handler/Iec104TcpSlaveHandler.java

@@ -0,0 +1,104 @@
+package com.ydl.iec.iec104.server.slave.handler;
+
+
+import com.ydl.iec.iec104.core.CachedThreadPool;
+import com.ydl.iec.iec104.core.ControlManageUtil;
+import com.ydl.iec.iec104.core.Iec104ThreadLocal;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.server.handler.ChannelHandlerImpl;
+import com.ydl.iec.iec104.server.handler.DataHandler;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+
+/**
+ * 
+* @ClassName: Iec104TcpSlaveHandler  
+* @Description: 从机处理类 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class Iec104TcpSlaveHandler extends SimpleChannelInboundHandler<MessageDetail> {
+	
+	private DataHandler dataHandler;
+	
+
+	public Iec104TcpSlaveHandler(DataHandler dataHandler) {
+		this.dataHandler = dataHandler;
+	}
+
+    /**
+     * 新连接
+     */
+    @Override
+    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+        Iec104ThreadLocal.setControlPool(new ControlManageUtil(ctx).setFrameAmountMax(Iec104ThreadLocal.getIec104Conig().getFrameAmountMax()));
+        /**
+         * 启动
+         */
+        Iec104ThreadLocal.getControlPool().startSendFrameTask();
+        if (dataHandler != null) {
+            Runnable runnable = () -> {
+                try {
+                    dataHandler.handlerAdded(new ChannelHandlerImpl(ctx));
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            };
+
+            CachedThreadPool.getCachedThreadPool().execute(runnable);
+        }
+    }
+
+    /**
+     * 断开连接
+     */
+    @Override
+    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+    }
+    
+    /**
+     * 收到消息
+     */
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, MessageDetail ruleDetail104) throws Exception {
+    	if (dataHandler != null) {
+            Runnable runnable = () -> {
+                try {
+                    dataHandler.channelRead(new ChannelHandlerImpl(ctx), ruleDetail104);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            };
+            CachedThreadPool.getCachedThreadPool().execute(runnable);
+    	}
+    }
+
+    /**
+     * 连接
+     * 
+     */
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception { 
+        Channel incoming = ctx.channel();
+        System.err.println("SimpleChatClient:" + incoming.remoteAddress() + "在线");
+    }
+    
+    /**
+     * 关闭
+     */
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception { 
+        Channel incoming = ctx.channel();
+        System.err.println("SimpleChatClient:" + incoming.remoteAddress() + "掉线");
+    }
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
+        Channel incoming = ctx.channel();
+        System.err.println("SimpleChatClient:" + incoming.remoteAddress() + "异常");
+        // 当出现异常就关闭连接
+        cause.printStackTrace();
+        ctx.close();
+    }
+}

+ 89 - 0
platform-iec/src/main/java/com/ydl/iec/iec104/server/slave/handler/SysUframeServerHandler.java

@@ -0,0 +1,89 @@
+package com.ydl.iec.iec104.server.slave.handler;
+
+import com.ydl.iec.iec104.common.BasicInstruction104;
+import com.ydl.iec.util.Iec104Util;
+import com.ydl.iec.iec104.enums.UControlEnum;
+import com.ydl.iec.util.ByteUtil;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+* @ClassName: SysUframeInboundHandler  
+* @Description: 处理U帧的报文 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class SysUframeServerHandler extends ChannelInboundHandlerAdapter {
+	
+	private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);
+	
+	/**
+	 * 拦截系统消息 
+	 */
+	@Override
+	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+		ByteBuf result = (ByteBuf) msg;
+		byte[] bytes = new byte[result.readableBytes()];
+		result.readBytes(bytes);
+		if (isSysInstruction(bytes)) {
+			UControlEnum uControlEnum = Iec104Util.getUcontrol(ByteUtil.getByte(bytes, 2, 4));
+			if (uControlEnum != null) {
+				uInstructionHandler(ctx, result, uControlEnum);
+				return;
+			}
+		} 
+		result.writeBytes(bytes);
+//		LOGGER.info("普通指令");
+		ctx.fireChannelRead(result);
+	}
+
+	/**
+	 * 
+	* @Title: isSysInstruction  
+	* @Description: TODO  判断是否是 系统报文
+	* @param @param bytes
+	* @param @return 
+	* @return boolean   
+	* @throws
+	 */
+	private boolean isSysInstruction(byte[] bytes) {
+		// U帧只有6字节
+		return bytes.length == 6;
+	}
+	
+	/**
+	 * 
+	* @Title: uInstructionHandler  
+	* @Description: 处理U帧 
+	* @param ctx
+	* @param result
+	* @param uControlEnum 
+	* @return void   
+	* @throws
+	 */
+	private void uInstructionHandler(ChannelHandlerContext ctx, ByteBuf result, UControlEnum uControlEnum) {
+		result.readBytes(new byte[result.readableBytes()]);
+		byte[] resultBytes = null;
+		if (uControlEnum == UControlEnum.TESTFR) {
+			LOGGER.error("收到测试指令");
+			resultBytes = BasicInstruction104.TESTFR_YES;
+		} else if (uControlEnum == UControlEnum.STOPDT) {
+			LOGGER.error("收到停止指令");
+			resultBytes =  BasicInstruction104.STOPDT_YES;
+		} else if (uControlEnum == UControlEnum.STARTDT) {
+			LOGGER.error("收到启动指令");
+			resultBytes = BasicInstruction104.STARTDT_YES;
+		} else {
+			LOGGER.error("U报文无效" +  uControlEnum);
+		} 
+		if (resultBytes != null) {
+			result.writeBytes(resultBytes);
+			LOGGER.error("回复U报");
+			ctx.writeAndFlush(result);
+		} 
+	}
+}

+ 251 - 0
platform-iec/src/main/java/com/ydl/iec/util/ByteUtil.java

@@ -0,0 +1,251 @@
+package com.ydl.iec.util;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * 
+* @ClassName: ByteUtil  
+* @Description: byte 工具类 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class ByteUtil {
+	
+	/**
+	 * 
+	* @Title: intToByteArray  
+	* @Description: int 转换成 byte数组 
+	* @param @param i
+	* @param @return 
+	* @return byte[]   
+	* @throws
+	 */
+	public static byte[] intToByteArray(int i) {
+        byte[] result = new byte[4];
+        result[0] = (byte) ((i >> 24) & 0xFF);
+        result[1] = (byte) ((i >> 16) & 0xFF);
+        result[2] = (byte) ((i >> 8) & 0xFF);
+        result[3] = (byte) (i & 0xFF);
+        return result;
+    }
+	
+	/**
+	* @Title: shortToByteArray  
+	* @Description: short 转换成 byte[] 
+	* @param @param val
+	* @param @return 
+	* @return byte[]   
+	* @throws
+	 */
+	public static byte[] shortToByteArray(short val) {
+		byte[] b = new byte[2];
+		b[0] = (byte) ((val >> 8) & 0xff);
+		b[1] = (byte) (val & 0xff);
+		return b;
+	}
+	
+	/**
+	 * 
+	* @Title: byteArrayToInt  
+	* @Description: byte[] 转换成 int
+	* @param @param bytes
+	* @param @return 
+	* @return int   
+	* @throws
+	 */
+	public static int byteArrayToInt(byte[] bytes) {
+        int value = 0;
+        for (int i = 0; i < 4; i++) {
+            int shift = (3 - i) * 8;
+            value += (bytes[i] & 0xFF) << shift;
+        }
+        return value;
+    }
+	
+	/**
+	 * 
+	* @Title: byteArrayToShort  
+	* @Description: byte[] 转换成short 
+	* @param @param bytes
+	* @param @return 
+	* @return short   
+	* @throws
+	 */
+	public static short byteArrayToShort(byte[] bytes) {
+        short value = 0;
+        for (int i = 0; i < 2; i++) {
+            int shift = (1 - i) * 8;
+            value += (bytes[i] & 0xFF) << shift;
+        }
+        return value;
+    }
+	
+	
+//	/**
+//	 * 
+//	* @Title: listToBytes  
+//	* @Description: TODO 
+//	* @param @param byteList
+//	* @param @return 
+//	* @return byte[]   
+//	* @throws
+//	 */
+//	public static byte[] listToBytes(List<Byte> byteList) {
+//		byte[] bytes = new byte[byteList.size()];
+//		int index = 0;
+//		for (Byte item : byteList) {
+//			bytes[index++] = item;
+//		}
+//		return bytes;
+//	}
+	
+	/**
+	 * 
+	* @Title: date2HByte  
+	* @Description: 日期转换成 CP56Time2a
+	* @param @param date
+	* @param @return 
+	* @return byte[]   
+	* @throws
+	 */
+    public static byte[] date2Hbyte(Date date) {
+    	ByteArrayOutputStream bOutput = new ByteArrayOutputStream();
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        // 毫秒需要转换成两个字节其中 低位在前高位在后 
+        // 先转换成short
+        int millisecond = calendar.get(Calendar.SECOND) * 1000 + calendar.get(Calendar.MILLISECOND);
+        
+        // 默认的高位在前
+        byte[] millisecondByte = intToByteArray(millisecond);
+        bOutput.write((byte) millisecondByte[3]);
+        bOutput.write((byte) millisecondByte[2]);
+        
+        // 分钟 只占6个比特位 需要把前两位置为零 
+        bOutput.write((byte) calendar.get(Calendar.MINUTE));
+        // 小时需要把前三位置零
+        bOutput.write((byte) calendar.get(Calendar.HOUR_OF_DAY));
+        // 星期日的时候 week 是0 
+        int week = calendar.get(Calendar.DAY_OF_WEEK);
+        if (week == Calendar.SUNDAY) {
+        	week = 7;
+        } else {
+        	week--;
+        } 
+        // 前三个字节是 星期 因此需要将星期向左移5位  后五个字节是日期  需要将两个数字相加 相加之前需要先将前三位置零
+        bOutput.write((byte) (week << 5) + (calendar.get(Calendar.DAY_OF_MONTH)));
+        // 前四字节置零
+        bOutput.write((byte) ((byte) calendar.get(Calendar.MONTH) + 1));
+        bOutput.write((byte) (calendar.get(Calendar.YEAR) - 2000));
+        return bOutput.toByteArray();
+    }
+    
+    
+    /**
+	 * 
+	* @Title: date2HByte  
+	* @Description:CP56Time2a转换成  时间
+	* @param @param date
+	* @param @return 
+	* @return byte[]   
+	* @throws
+	 */
+    public static Date  byte2Hdate(byte[] dataByte) {
+        int year = (dataByte[6] & 0x7F) + 2000;
+        int month = dataByte[5] & 0x0F;
+        int day = dataByte[4] & 0x1F;
+        int hour = dataByte[3] & 0x1F;
+        int minute = dataByte[2] & 0x3F;
+        int second = dataByte[1] > 0 ? dataByte[1] : (int) (dataByte[1] & 0xff);
+        int millisecond = dataByte[0] > 0 ? dataByte[0] : (int) (dataByte[0] & 0xff);
+        millisecond = (second << 8) + millisecond;
+        second = millisecond / 1000;
+        millisecond = millisecond % 1000;
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.YEAR, year);
+        calendar.set(Calendar.MONTH, month);
+        calendar.set(Calendar.DAY_OF_MONTH, day);
+        calendar.set(Calendar.HOUR_OF_DAY, hour);
+        calendar.set(Calendar.MINUTE, minute);
+        calendar.set(Calendar.SECOND, second);
+        calendar.set(Calendar.MILLISECOND, millisecond);
+        return calendar.getTime();
+    }
+
+	public static String byteArrayToHexString(byte[] array) {
+        return byteArray2HexString(array, Integer.MAX_VALUE, false);
+    }
+
+	public static String byteArray2HexString(byte[] arrBytes, int count, boolean blank) {
+        String ret = "";
+        if (arrBytes == null || arrBytes.length < 1) {
+        	return ret;
+        }
+        if (count > arrBytes.length) {
+        	count = arrBytes.length;
+        }
+        StringBuilder builder = new StringBuilder();
+
+        for (int i = 0; i < count; i++) {
+            ret = Integer.toHexString(arrBytes[i] & 0xFF).toUpperCase();
+            if (ret.length() == 1) {
+            	builder.append("0").append(ret);
+            } else {
+            	builder.append(ret);
+            }
+            if (blank) {
+            	builder.append(" ");
+            }
+        }
+
+        return builder.toString();
+
+    }
+
+    /**
+     * 返回指定位置的数组
+     * @param bytes
+     * @param start 开始位置
+     * @param length  截取长度
+     * @return
+     */
+	public  static byte[] getByte(byte[] bytes, int start, int length) {
+		byte[] ruleByte = new byte[length];
+		int index = 0;
+		while (index < length) {
+			ruleByte[index++] = bytes[start++];
+		}
+		return ruleByte;
+	}
+
+
+    /**
+     * 十六进制字符串转换成byte数组
+     * @param hexStr
+     * @return
+     */
+	public static  byte[] hexStringToBytes(String hexStr){
+        hexStr = hexStr.replaceAll(" ", "");
+        hexStr = hexStr.toUpperCase();
+        int len = (hexStr.length() / 2);
+        byte[] result = new byte[len];
+        char[] achar = hexStr.toCharArray();
+        for (int i = 0; i < len; i++) {
+            int pos = i * 2;
+            result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
+        }
+        return result;
+    }
+
+    private static int toByte(char c) {
+        byte b = (byte) "0123456789ABCDEF".indexOf(c);
+        return b;
+    }
+
+
+    public static void main(String[] agrwu) {
+            System.out.println(byteArrayToHexString(hexStringToBytes("68 14 8c 00 02 00 1e 01 03 00 01 00 0a 00 00 2d ad 28 0b 1c 09 14")));
+    }
+}

+ 252 - 0
platform-iec/src/main/java/com/ydl/iec/util/Iec104Util.java

@@ -0,0 +1,252 @@
+package com.ydl.iec.util;
+
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.enums.TypeIdentifierEnum;
+import com.ydl.iec.iec104.enums.UControlEnum;
+
+/**
+ * 
+* @ClassName: Iec104Util
+* @Description: 工具类 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class Iec104Util {
+	
+	private static int controlLength = 4;
+	
+	/**
+	 * I 格式 低位在前
+	 * @param accept 接收序列号
+	 * @param send 发送序列号
+	 * @return
+	 */
+	public static byte[] getIcontrol(short accept, short send) {
+		byte[] control = new byte[4];
+		// 向左移动一位 保证低位的D0 是0
+		send = (short) (send << 1);
+		control[0] =  (byte) ((send));
+		control[1]  =  (byte) ((send >> 8));
+		accept = (short) (accept << 1);
+		control[2] =   (byte) ((accept));
+		control[3]  =  (byte) ((accept >> 8));
+		return control;
+	}
+	
+	/**
+	 * 返回控制域中的接收序号
+	 * @param control
+	 * @return
+	 */
+	public  static short getAccept(byte[] control) {
+		int accept = 0;
+		short  acceptLow =   (short) (control[2] & 0xff);
+		short  acceptHigh =   (short) (control[3] & 0xff);
+		accept += acceptLow;
+		accept += acceptHigh << 8;
+		accept = accept >> 1;
+		return (short) accept;
+		
+	}
+	
+	/**
+	 * 返回控制域中的发送序号
+	 * @param control
+	 * @return
+	 */
+	public  static short getSend(byte[] control) {
+		int send = 0;
+		short  acceptLow =  (short) (control[0] & 0xff);
+		short  acceptHigh =  (short) (control[1] & 0xff);
+		send += acceptLow;
+		send += acceptHigh << 8;
+		send = send >> 1;
+		return (short) send;
+	}
+	
+	/**
+	 * S 格式
+	 * @param accept
+	 * @return
+	 */
+	public static byte[] getScontrol(short accept) {
+		byte[] control = new byte[4];
+		// 向左移动一位 保证低位的D0 是0
+		short send = 1;
+		control[0] =  (byte) ((send));
+		control[1]  =  (byte) ((send >> 8));
+		accept = (short) (accept << 1);
+		control[2] =   (byte) ((accept));
+		control[3]  =  (byte) ((accept >> 8));
+		return control;
+	}
+
+	/**
+	 * 
+	* @Title: 返回U帧
+	* @Description: 判断是否是
+	* @param @param control
+	* @param @return 
+	* @return boolean   
+	* @throws
+	 */
+	public static UControlEnum getUcontrol(byte[] control) {
+		if (control.length < controlLength || control[1] != 0 || control[3] != 0 || control[2] != 0) {
+			return null;
+		}  
+		int controlInt = ByteUtil.byteArrayToInt(control);
+		for (UControlEnum ucontrolEnum : UControlEnum.values()) {
+			if (ucontrolEnum.getValue() == controlInt) {
+				return ucontrolEnum;
+			}
+		}
+		return null;
+	}
+	
+	
+	
+	
+	
+	/**
+	 * 返回消息地址 其中低位在前
+	 * @param i
+	 * @return
+	 */
+	public static byte[] intToMessageAddress(int i) {
+        byte[] result = new byte[3];
+        result[0] = (byte) (i & 0xFF);
+        result[1] = (byte) ((i >> 8) & 0xFF);
+        result[2] = (byte) ((i >> 16) & 0xFF);
+        return result;
+    }
+	
+	
+	
+	/**
+	 * 消息地址 只有三个
+	 * @param bytes
+	 * @return
+	 */
+	public static int messageAddressToInt(byte[] bytes) {
+        int value = 0;
+        for (int i = 2; i >= 0; i--) {
+            int shift = (2 - i) * 8;
+            value += (bytes[2 - i] & 0xFF) << shift;
+        }
+        return value;
+    }
+	
+	/**
+	 * 设置可以变限定词
+	 * @param ruleDetail104
+	 * @param byteItem
+	 */
+	public static void setChanged(MessageDetail ruleDetail104, byte byteItem) {
+		// 第一位是 0 则是有序的
+		ruleDetail104.setContinuous((byteItem & 0x80) == 0 ? false : true);
+		// 先将第一位数置零 然后转换成int
+		ruleDetail104.setMeasgLength(byteItem & (byte) 0x7F);
+	}
+	    
+	/**	
+	 * 返回可变限定词数组
+	 * @param ruleDetail104
+	 * @return
+	 */
+	public static byte getChangedQualifiers(MessageDetail ruleDetail104) {
+		// 将长度转换成 byte
+		byte changedQualifiers = (byte) ruleDetail104.getMeasgLength();
+		// 判断SQ 置   isContinuous false SQ = 0;否则 SQ =1 ,  同时将SQ置 设置在 可变限定词的 D7位置
+		int sq = ruleDetail104.isContinuous() ?  0x80 : 0;
+		changedQualifiers = (byte) (sq | changedQualifiers);
+		return changedQualifiers;
+	} 
+	
+	
+	public static void setMeaageAttribute(MessageDetail ruleDetail104) {
+		boolean isMessage =  !(TypeIdentifierEnum.generalCall.equals(ruleDetail104.getTypeIdentifier())  //总召唤无此项
+				|| TypeIdentifierEnum.timeSynchronization.equals(ruleDetail104.getTypeIdentifier()) // 时钟同步
+				|| TypeIdentifierEnum.resetPprocess.equals(ruleDetail104.getTypeIdentifier()) // 复位进程
+				|| TypeIdentifierEnum.initEnd.equals(ruleDetail104.getTypeIdentifier()));
+		ruleDetail104.setMessage(isMessage);
+		
+		boolean isQualifiers = !(TypeIdentifierEnum.timeSynchronization.equals(ruleDetail104.getTypeIdentifier())  // 时钟同步
+				|| TypeIdentifierEnum.onePointTeleindication.equals(ruleDetail104.getTypeIdentifier()) //单点摇信
+				|| TypeIdentifierEnum.twoPointTeleindication.equals(ruleDetail104.getTypeIdentifier()) // 双点摇信
+				|| TypeIdentifierEnum.onePointTelecontrol.equals(ruleDetail104.getTypeIdentifier()) // 单命令遥控
+				|| TypeIdentifierEnum.twoPointTelecontrol.equals(ruleDetail104.getTypeIdentifier())); // 双命令遥控
+		ruleDetail104.setQualifiers(isQualifiers);
+		boolean isTimeScale = TypeIdentifierEnum.timeSynchronization.equals(ruleDetail104.getTypeIdentifier())  // 时钟同步
+				|| TypeIdentifierEnum.onePointTimeTeleindication.equals(ruleDetail104.getTypeIdentifier()) // 摇信带时标 单点
+				|| TypeIdentifierEnum.twoPointTimeTeleindication.equals(ruleDetail104.getTypeIdentifier()); //摇信带时标 双点
+		ruleDetail104.setTimeScaleExit(isTimeScale);
+	}
+	
+	/**
+	 * short 转换成两个 字节后是163  00    也就是  value[1] 中才有值
+	 * test 在D7位置 因此 值应该和  01000000 做与运算
+	 * P/N 0肯定确认  1否定确认
+	 * @return  肯定或否定确认
+	 */
+	public static boolean isYes(byte[] values) {
+		return (values[0] & 1 << 6) == 0;
+	}
+	/**
+	 *  short 转换成两个 字节后是163  00     也就是  value[1] 中才有值
+	 *  test 在D7位置 因此 值应该和 10000000 做与运算
+	 *  tets 0 为试验  1 试验
+	 * @return 是否试验
+	 */
+	public static boolean isTets(byte[] values) {
+		return (values[0] & 1 << 7) != 0;
+	}
+	
+	/**
+	 * 返回具体的原因
+	 * @param values
+	 * @return
+	 */
+	public static short getTransferReasonShort(byte[] values) {
+		byte transferReason = values[0];
+		// 前两位置零
+		transferReason = (byte) (transferReason & 0x3E);
+		return transferReason;
+	}
+	
+	
+	public static short getTransferReasonShort(boolean isTets, boolean isYes, short transferReason) {
+		int t = isTets ? 1 : 0;
+		int y = isYes ? 0 : 1;
+		int transferReasonInt = t << 7 | transferReason;
+		transferReasonInt = y << 6 | transferReasonInt;
+		
+		short transferReasonShort = (short) (transferReasonInt << 8);
+		return transferReasonShort;
+	}
+	
+	
+	/**
+	 *  返回终端地址对应的byte数组 其中低位在前
+	 * @param terminalAddress
+	 * @return
+	 */
+	public static byte[] getTerminalAddressByte(short terminalAddress) {
+		byte[] b = new byte[2];
+		b[1] = (byte) ((terminalAddress >> 8) & 0xff);
+		b[0] = (byte) (terminalAddress & 0xff);
+		return b;
+	}
+	
+	
+	/**
+	 *	返回回终端地址 其中低位在前
+	 * @param terminalAddress
+	 * @return
+	 */
+	public static short getTerminalAddressShort(byte[] terminalAddress) {
+		 short value = 0;
+		 value += (terminalAddress[0] & 0xFF);
+		 value += (terminalAddress[1] & 0xFF) << 8;
+		 return value;
+	}
+}

+ 52 - 0
platform-iec/src/test/java/com/ydl/iec/iec104/core/Decoder104Test.java

@@ -0,0 +1,52 @@
+package com.ydl.iec.iec104.core;
+
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.util.Encoder104UtilTest;
+import com.ydl.iec.util.ByteUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class Decoder104Test {
+
+
+
+
+    @Test
+    public  void tetsSoe() throws IOException {
+        MessageDetail messageDetail = Encoder104UtilTest.customInfo();
+        byte[] bytes = Encoder104.encoder(messageDetail);
+        System.err.println(ByteUtil.byteArrayToHexString(bytes));
+        messageDetail = Decoder104.encoder(bytes);
+        System.err.println(ByteUtil.byteArrayToHexString(Encoder104.encoder(messageDetail)));
+
+
+//        68
+//        16
+//        08 00 02 00
+//        0D  可变结构限定词 1字节 
+//        02 01 输原因 2
+//        00 01  应用服务数据单
+//        00 03 00
+//        00 12
+//        34 00 00
+//        00 00
+//        56
+//        7800
+
+
+
+        //68
+        // 14  
+        // 8c 00 02 00  
+        // 1e 类型标识TI 带CP56Time2a时标的单点信息(遥信带时标  1个字节
+        // 01  可变结构限定词 1字节          可变结构限定词 0000 0001  不连续  1个结构
+        // 03 00  0000 0011 传输原因 2个字节 突发
+        // 01 00  应用服务数据单元公共地址  2个字节
+        // 0a 00 00  信息对象地址 3个字节
+        // 2d 消息元素  1个字节
+        // ad 28 0b 1c 09 14  时标7个字节 缺少一个
+//        Decoder104.encoder(ByteUtil.hexStringToBytes("68 14 8c 00 02 00 1e 01 03 00 01 00 0a 00 00 2d ad 28 0b 1c 09 14"));
+    }
+
+}

+ 19 - 0
platform-iec/src/test/java/com/ydl/iec/iec104/server/master/Iec104TcpMasterClientTest.java

@@ -0,0 +1,19 @@
+package com.ydl.iec.iec104.server.master;
+
+
+import com.ydl.iec.iec104.config.Iec104Config;
+import com.ydl.iec.iec104.server.Iec104MasterFactory;
+import org.junit.Test;
+
+public class Iec104TcpMasterClientTest {
+
+	@Test
+	public void test() throws  Exception {
+		Iec104Config iec104Config  = new Iec104Config();
+		iec104Config.setFrameAmountMax((short) 1);
+		iec104Config.setTerminnalAddress((short) 1);
+		Iec104MasterFactory.createTcpClientMaster("127.0.0.1", 2404).setDataHandler(new MasterSysDataHandler()).setConfig(iec104Config).run();
+        Thread.sleep(1000000);
+	}
+
+}

+ 79 - 0
platform-iec/src/test/java/com/ydl/iec/iec104/server/master/MasterSysDataHandler.java

@@ -0,0 +1,79 @@
+package com.ydl.iec.iec104.server.master;
+
+import com.ydl.iec.iec104.common.BasicInstruction104;
+import com.ydl.iec.iec104.core.CachedThreadPool;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.message.MessageInfo;
+import com.ydl.iec.iec104.server.handler.ChannelHandler;
+import com.ydl.iec.iec104.server.handler.DataHandler;
+import com.ydl.iec.util.ByteUtil;
+import com.ydl.iec.util.Iec104Util;
+import lombok.extern.slf4j.Slf4j;
+
+
+@Slf4j
+public class MasterSysDataHandler implements DataHandler {
+
+	@Override
+	public void handlerAdded(ChannelHandler ctx) throws Exception {
+		Runnable runnable = () -> {
+			try {
+				Thread.sleep(5000L);
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+			System.err.println("___________________________");
+			ctx.writeAndFlush(BasicInstruction104.getGeneralCallRuleDetail104());
+		};
+		CachedThreadPool.getCachedThreadPool().execute(runnable);
+//		ctx.writeAndFlush(BasicInstruction104.getEndGeneralCallRuleDetail104());
+	}
+
+	@Override
+	public void channelRead(ChannelHandler ctx, MessageDetail ruleDetail104) throws Exception {
+		log.info("启动字符:" + ruleDetail104.getStart());
+		log.info("字节长度:" + ruleDetail104.getApuuLength());
+		byte[] control  = ruleDetail104.getControl();
+		log.info("控制域:Accept" + Iec104Util.getAccept(control));
+		log.info("控制域:getSend" + Iec104Util.getSend(control));
+		log.info("类型标识:" + ruleDetail104.getTypeIdentifier().getValue());
+		log.info("可变结构限定词:" + ruleDetail104.isContinuous());
+		log.info("数据长度:" + ruleDetail104.getMeasgLength());
+		log.info("传输原因:" + ruleDetail104.getTransferReason());
+		log.info("终端地址:" + ruleDetail104.getTerminalAddress());
+		log.info("消息地址:" + ruleDetail104.getMessageAddress());
+		log.info("消息结构:" + ruleDetail104.getMessages());
+		log.info("是否有消息元素:" + ruleDetail104.isMessage());
+		log.info("判断是否有限定词:" + ruleDetail104.getQualifiersType());
+		log.info("判断是否有时标:" + ruleDetail104.isTimeScaleExit());
+		log.info("判断消息是否连续:" + ruleDetail104.isContinuous());
+		if(ruleDetail104.getMeasgLength()>0){
+			for (int i = 0; i<ruleDetail104.getMeasgLength();i++) {
+				MessageInfo messageInfo = ruleDetail104.getMessages().get(i);
+				log.info(i + "-消息地址:" + messageInfo.getMessageAddress());
+				log.info(i + "-数据:" + ByteUtil.byteArrayToHexString(messageInfo.getMessageInfos()));
+			}
+		}
+	/*	try {
+			log.info("是否有消息元素:" + ruleDetail104.getQualifiersType().getValue());
+		}catch (Exception e){
+			e.printStackTrace();
+		}*/
+		if(ruleDetail104.getQualifiersType() != null){
+			log.info("限定词:" + ruleDetail104.getQualifiersType().getValue());
+		}else{
+			log.info("限定词: 无");
+		}
+		log.info("时标:" + ruleDetail104.getTimeScale());
+		log.info("限定词:" + ruleDetail104.getHexString());
+
+		System.out.println(ruleDetail104);
+		System.err.print("收到消息");
+	}
+
+	public static void main(String[] args) {
+		byte[] bytes = {1};
+		String strContent = ByteUtil.byteArrayToHexString(bytes);
+		System.out.println(strContent);
+	}
+}

+ 34 - 0
platform-iec/src/test/java/com/ydl/iec/iec104/server/slave/Iec104TcpServerSlaveTest.java

@@ -0,0 +1,34 @@
+package com.ydl.iec.iec104.server.slave;
+
+
+import com.ydl.iec.iec104.config.Iec104Config;
+import com.ydl.iec.iec104.server.Iec104SlaveFactory;
+import org.junit.Test;
+
+/**
+ * 
+* @ClassName: Iec104TcpServerSlaveTest  
+* @Description: 测试 iec104 协议TCP传输方式服务端做从机服务
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class Iec104TcpServerSlaveTest {
+
+	/**
+	 * 
+	* @Title: test   
+	* @Description: 测试 iec104 协议TCP传输方式服务端做从机服务
+	* @param @throws Exception 
+	* @return void   
+	* @throws
+	 */
+	@Test
+	public void test() throws Exception {
+		Iec104Config iec104Config  = new Iec104Config();
+		iec104Config.setFrameAmountMax((short) 1);
+		iec104Config.setTerminnalAddress((short) 1);
+		Iec104SlaveFactory.createTcpServerSlave(2404).setDataHandler(new SysDataHandler()).setConfig(iec104Config).run();
+//        Thread.sleep(1000000);
+	}
+
+}

+ 51 - 0
platform-iec/src/test/java/com/ydl/iec/iec104/server/slave/SysDataHandler.java

@@ -0,0 +1,51 @@
+package com.ydl.iec.iec104.server.slave;
+
+import com.ydl.iec.iec104.common.BasicInstruction104;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.server.handler.ChannelHandler;
+import com.ydl.iec.iec104.server.handler.DataHandler;
+import lombok.extern.slf4j.Slf4j;
+
+
+@Slf4j
+public class SysDataHandler implements DataHandler {
+
+	@Override
+	public void handlerAdded(ChannelHandler ctx) throws Exception {
+		ctx.writeAndFlush(BasicInstruction104.getEndGeneralCallRuleDetail104());
+	}
+
+	@Override
+	public void channelRead(ChannelHandler ctx, MessageDetail ruleDetail104) throws Exception {
+		log.info("启动字符:" + ruleDetail104.getStart());
+//		log.info("字节长度:" + ruleDetail104.getApuuLength());
+//		log.info("控制域:" + ruleDetail104.getControl());
+//		log.info("类型标识:" + ruleDetail104.getTypeIdentifier().getValue());
+//		log.info("可变结构限定词:" + ruleDetail104.isContinuous());
+//		log.info("数据长度:" + ruleDetail104.getMeasgLength());
+//		log.info("传输原因:" + ruleDetail104.getTransferReason());
+//		log.info("终端地址:" + ruleDetail104.getTerminalAddress());
+//		log.info("消息地址:" + ruleDetail104.getMessageAddress());
+//		log.info("消息结构:" + ruleDetail104.getMessages());
+//		log.info("是否有消息元素:" + ruleDetail104.isMessage());
+//		log.info("判断是否有限定词:" + ruleDetail104.getQualifiersType());
+//		log.info("判断是否有时标:" + ruleDetail104.isTimeScaleExit());
+//		log.info("判断消息是否连续:" + ruleDetail104.isContinuous());
+//		if(ruleDetail104.getMeasgLength()>0){
+//			for (int i = 0; i<ruleDetail104.getMeasgLength();i++) {
+//				log.info(String.valueOf(ruleDetail104.getMessages().get(i)));
+//			}
+//		}
+//		try {
+//			log.info("是否有消息元素:" + ruleDetail104.getQualifiersType().getValue());
+//		}catch (Exception e){}
+//
+//		log.info("限定词:" + ruleDetail104.getQualifiersType().getValue());
+//		log.info("时标:" + ruleDetail104.getTimeScale());
+//		log.info("限定词:" + ruleDetail104.getHexString());
+//
+//		System.out.println(ruleDetail104);
+//		System.err.print("收到消息");
+	}
+
+}

+ 394 - 0
platform-iec/src/test/java/com/ydl/iec/iec104/util/Encoder104UtilTest.java

@@ -0,0 +1,394 @@
+package com.ydl.iec.iec104.util;
+
+import com.ydl.iec.iec104.core.Decoder104;
+import com.ydl.iec.iec104.core.Encoder104;
+import com.ydl.iec.iec104.enums.QualifiersEnum;
+import com.ydl.iec.iec104.enums.TypeIdentifierEnum;
+import com.ydl.iec.iec104.enums.UControlEnum;
+import com.ydl.iec.iec104.message.MessageDetail;
+import com.ydl.iec.iec104.message.MessageInfo;
+import com.ydl.iec.util.ByteUtil;
+import com.ydl.iec.util.Iec104Util;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(Parameterized.class)
+public class Encoder104UtilTest {
+	
+	private MessageDetail originalObj;
+	private String name;
+	
+	public Encoder104UtilTest(Object originalObj, String name) {
+		this.originalObj = (MessageDetail) originalObj;
+		this.name = name; 
+	}
+	@SuppressWarnings("rawtypes")
+	@Parameterized.Parameters
+	public static Collection primeNumbers() {
+		return Arrays.asList(new Object[][] {
+			{getGeneralCallRuleDetail104(), "总召唤指令"},
+//			{getEndGeneralCallRuleDetail104(), "结束总召唤指令"},
+//			{getYesGeneralCallRuleDetail104(), "总召唤确认指令"},
+//			{getInitRuleDetail104(), "初始化指令"},
+//			{getInitYesRuleDetail104(), "初始化确认指令"},
+//			{getInitEndRuleDetail104(), "初始化结束指令"},
+//			{getSRuleDetail104(), "S帧指令"},
+//				{getPointInfo(), "点位指令"},
+		});
+	}
+	
+	@Test
+	public void testEncoder() throws IOException {
+		byte[] bytes = Encoder104.encoder(originalObj);
+		System.err.println(name + " 16:" + ByteUtil.byteArrayToHexString(bytes) +  " length " + bytes.length);
+		MessageDetail oldObj = Decoder104.encoder(bytes);
+		judge(originalObj, oldObj);
+//		assertEquals(name + " 长度和预计十六进制输出不匹配", Util.byteArrayToHexString(bytes).length(), originalObj.getHexString().length());
+		assertEquals(name + "和预计十六进制输出不匹配", ByteUtil.byteArrayToHexString(bytes).toUpperCase(), originalObj.getHexString().toUpperCase());
+	}
+	
+	
+	/**
+	 *  
+	 *     类型标识 : 召唤命令
+	 * @return
+	 */
+	private static MessageDetail getGeneralCallRuleDetail104() {
+		TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.generalCall;
+		//SQ=0 length =1
+		//联系
+		int sq = 1;
+		boolean isContinuous = sq == 0 ? false : true;
+		// 接收序号
+		short accept = 0;
+		// 发送序号
+		short send = 0;
+		byte[] control = Iec104Util.getIcontrol(accept, send);
+		// 传输原因
+		short transferReason = 6;
+		// true:1 ; false : 0
+		boolean isTest = false;
+		// true:0 false;1
+		boolean isPN = true;
+
+		short terminalAddress = 1;
+		// 消息地址 总召唤地址为0
+		int messageAddress = 0;
+
+		QualifiersEnum qualifiers = QualifiersEnum.generalCallQualifiers;
+		List<MessageInfo> messages = new ArrayList<>();
+//		MessageInfo message = new MessageInfo();
+//		// 不写
+//		message.setQualifiersType(qualifiers);
+//		message.setMessageInfos(new byte[] {});
+//
+//		messages.add(message);
+		MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason,
+				terminalAddress, messageAddress, messages, null, qualifiers);
+//
+//		68 0E 0000 0000  64(类型标识) 01(可变结构限定词)0600(传输原因)0100(公共地址)0000(信息体地址)0020
+//		68 0E 0000 0000  64         80             06000        1000        00000  20                 20
+		ruleDetail104.setHexString("680E0000000064800600010000000020");
+		return ruleDetail104;
+	}
+	
+	
+	private static MessageDetail getYesGeneralCallRuleDetail104() {
+		TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.generalCall; 
+		 //SQ=0 length =1
+		int sq = 1;
+		boolean isContinuous = sq == 0 ? false : true;
+		// 接收序号
+		short accept = 0;
+		// 发送序号
+		short send = 0;
+		byte[] control = Iec104Util.getIcontrol(accept, send);
+		// 传输原因
+		short transferReason = 7;
+		// true:1 ; false : 0
+		boolean isTest = false;
+		// true:0 false;1
+		boolean isPN = true;
+		
+		short terminalAddress = 1;
+		// 消息地址 总召唤地址为0
+		int messageAddress = 0;
+		
+		QualifiersEnum qualifiers = QualifiersEnum.generalCallQualifiers;
+//		List<MessageInfo> messages = new ArrayList<>();
+		MessageInfo message = new MessageInfo();
+//		message.setQualifiersType(qualifiers);
+//		message.setMessageInfos(new byte[] {});
+		
+//		messages.add(message);
+		MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason,
+				terminalAddress, messageAddress, new ArrayList<>(), null, qualifiers);
+//
+//      68 0E 0000 0000  64          80             0700         0100        0000          0020
+//		68 0E 0000 0000  64(类型标识) 01(可变结构限定词)0600(传输原因)0100(公共地址)0000(信息体地址)0020
+		ruleDetail104.setHexString("680E0000000064800700010000000020");
+		return ruleDetail104;
+	}
+	
+	/**
+	 * 	结束
+	 * @return
+	 */
+	private static MessageDetail getEndGeneralCallRuleDetail104() {
+		TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.generalCall; 
+		 //SQ=0 length =1
+		int sq = 0;
+		boolean isContinuous = sq == 0 ? false : true;
+		// 接收序号
+		short accept = 1;
+		// 发送序号
+		short send = 4;
+//		int control = 0x08000200;
+		
+		byte[] control = Iec104Util.getIcontrol(accept, send);
+		// 传输原因
+		short transferReason = 0x0A;
+		// true:1 ; false : 0
+		boolean isTest = false;
+		// true:0 false;1
+		boolean isPN = true;
+		
+		short terminalAddress = 1;
+		// 消息地址 总召唤地址为0
+		int messageAddress = 0;
+		
+		QualifiersEnum qualifiers = QualifiersEnum.generalCallQualifiers;
+		List<MessageInfo> messages = new ArrayList<>();
+		MessageInfo message = new MessageInfo();
+//		message.setQualifiersType(qualifiers);
+		message.setMessageInfos(new byte[] {});
+		
+		messages.add(message);
+		MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason,
+				terminalAddress, messageAddress, messages, null, qualifiers);
+//      68 0E 0800 0200 64 01 0A00 0100 000000 20
+//		68 0E 0800 0200 64 01 0A00 0100 000000 20
+		ruleDetail104.setHexString("680E0800020064010A00010000000020");
+		return ruleDetail104;
+	}
+	
+	/**
+	 * 初始化
+	 * @return
+	 */
+	private static MessageDetail getInitRuleDetail104() {
+		byte[] control = ByteUtil.intToByteArray(UControlEnum.STARTDT.getValue());
+		MessageDetail ruleDetail104 = new MessageDetail(control);
+		//6804 07(START命令) 00 0000
+		ruleDetail104.setHexString("680407000000");
+		return ruleDetail104;
+	}
+	
+	/**
+	 * 初始化确认
+	 * @return
+	 */
+	private static MessageDetail getInitYesRuleDetail104() {
+		byte[] control = ByteUtil.intToByteArray((UControlEnum.STARTDT_YES.getValue()));
+		MessageDetail ruleDetail104 = new MessageDetail(control);
+		//6804 07(START命令) 00 0000
+		ruleDetail104.setHexString("68040B000000");
+		return ruleDetail104;
+	}
+	
+	/**
+	 * 初始化完成
+	 * @return
+	 */
+	private static MessageDetail getInitEndRuleDetail104() {
+		TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.initEnd; 
+		 //SQ=0 length =1
+		int sq = 1;
+		boolean isContinuous = sq == 0 ? false : true;
+		// 接收序号
+		short accept = 0;
+		// 发送序号
+		short send = 0;
+		byte[] control = Iec104Util.getIcontrol(accept, send);
+		// 传输原因
+		short transferReason = 4;
+		// true:1 ; false : 0
+		boolean isTest = false;
+		// true:0 false;1
+		boolean isPN = true;
+		
+		short terminalAddress = 1;
+		// 消息地址 总召唤地址为0
+		int messageAddress = 0;
+		
+		QualifiersEnum qualifiers = QualifiersEnum.localMmanualResetQualifiers;
+		List<MessageInfo> messages = new ArrayList<>();
+		MessageInfo message = new MessageInfo();
+//		message.setQualifiersType(qualifiers);
+//		message.setMessageInfos(new byte[] {});
+//
+//		messages.add(message);
+		MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason,
+				terminalAddress, messageAddress, messages, null, qualifiers);
+//		68 0E 0000 0000  46          80          0400010000000001
+//		68 0E 0000 0000  46          01             0400         0100        0000          0001
+//		68 0E 0000 0000  64(类型标识) 01(可变结构限定词)0400(传输原因)0100(公共地址)0000(信息体地址)0020
+		ruleDetail104.setHexString("680E0000000046800400010000000001");
+		return ruleDetail104;
+	}
+	
+	/**
+	 * S 帧
+	 * @return
+	 */
+	private static MessageDetail getSRuleDetail104() {
+		short accept = 1; // 01 00 02 00
+//  	接收序号是1
+//		byte[] control = ByteUtil.intToByteArray(0x1000200);
+		byte[] control = Iec104Util.getScontrol(accept);
+		MessageDetail ruleDetail104 = new MessageDetail(control);
+		//6804 0100() 0200
+		ruleDetail104.setHexString("680401000200");
+		return ruleDetail104;
+	} 
+	
+	
+	
+	
+	
+	public void judge(MessageDetail originalObj, MessageDetail oldObj) {
+		assertEquals(name + ": start :", originalObj.getStart(), oldObj.getStart());
+		assertEquals(name + ": apuuLength :", originalObj.getApuuLength(), oldObj.getApuuLength());
+		
+		int index = 0;
+		while (index < originalObj.getControl().length) {
+			assertEquals(name + ": control :", originalObj.getControl()[index], oldObj.getControl()[index++]);
+		}
+		assertEquals(name + ": typeIdentifier :", originalObj.getTypeIdentifier(), oldObj.getTypeIdentifier());
+		assertEquals(name + ": isContinuous :", originalObj.isContinuous(), oldObj.isContinuous());
+		assertEquals(name + ": measgLength :", originalObj.getMeasgLength(), oldObj.getMeasgLength());
+		assertEquals(name + ": transferReason :", originalObj.getTransferReason(), oldObj.getTransferReason());
+		assertEquals(name + ": terminalAddress :", originalObj.getTerminalAddress(), oldObj.getTerminalAddress());
+		assertEquals(name + ": messageAddress :", originalObj.getMessageAddress(), oldObj.getMessageAddress());
+		assertEquals(name + ": qualifiersType :", originalObj.getQualifiersType(), oldObj.getQualifiersType());
+		assertEquals(name + ": timeScale :", originalObj.getTimeScale(), oldObj.getTimeScale());
+		assertEquals(name + ": messageAddress :", originalObj.getMessageAddress(), oldObj.getMessageAddress());
+		assertEquals(name + ": messageAddress :", originalObj.getMessageAddress(), oldObj.getMessageAddress());
+		assertEquals(name + ": messageAddress :", originalObj.getMessageAddress(), oldObj.getMessageAddress());
+		judgeMessages(originalObj, oldObj);
+		System.err.println(name + " 指令测试结束");
+	}
+
+	private void judgeMessages(MessageDetail originalObj, MessageDetail oldObj) {
+		assertEquals(name + ": messagesSize :", originalObj.getMessages().size(), oldObj.getMessages().size());
+		int index = 0;
+		while (index < originalObj.getMessages().size()) {
+			MessageInfo originalMessagesObj = originalObj.getMessages().get(index);
+			MessageInfo oldMessageObj = oldObj.getMessages().get(index);
+			
+//			assertEquals(name + ": Messages timeScale :", originalMessagesObj.getTimeScale(), oldMessageObj.getTimeScale());
+			Assert.assertEquals(name + ": Messages qualifiersType :", originalMessagesObj.getQualifiersType(), oldMessageObj.getQualifiersType());
+			assertEquals(name + ": Messages timeScale :", originalMessagesObj.getMessageInfos().length, oldMessageObj.getMessageInfos().length);
+			int messageInfoIndex = 0;
+			while (messageInfoIndex < originalMessagesObj.getMessageInfos().length) {
+				assertEquals(name + ": Messages messageInfos :", originalMessagesObj.getMessageInfos()[messageInfoIndex], oldMessageObj.getMessageInfos()[messageInfoIndex]);
+				messageInfoIndex++;
+			}
+			index++;
+		}
+	}
+
+	/**
+	 * 点位指令
+	 * @return
+	 */
+	private static MessageDetail getPointInfo() {
+		TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.onePointTelecontrol;
+		//SQ=0 length =1
+		int sq = 0;
+		boolean isContinuous = sq == 0 ? false : true;
+		// 接收序号
+		short accept = 0;
+		// 发送序号
+		short send = 0;
+		byte[] control = Iec104Util.getIcontrol(accept, send);
+		// 传输原因
+		short transferReason = 6;
+		// true:1 ; false : 0
+		boolean isTest = false;
+		// true:0 false;1
+		boolean isPN = true;
+
+		short terminalAddress = 1;
+		// 消息地址 总召唤地址为0
+		int messageAddress = 1;
+
+		List<MessageInfo> messages = new ArrayList<>();
+		MessageInfo message = new MessageInfo();
+		message.setMessageInfos(new byte[] {0x00});
+		message.setMessageAddress(messageAddress);
+		messages.add(message);
+		//
+		MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason,
+				terminalAddress, 0, messages, null, null);
+		//6804 07(START命令) 00 0000
+
+
+//		        68 0e 0000 0000  2d          01             0600        0100         0100          0000
+		//		68 0E 0000 0000  2d(类型标识) 01(可变结构限定词)0600(传输原因)0100(公共地址)0000(信息体地址)0000
+		ruleDetail104.setHexString("680e000000002d010600010001000000");
+		return ruleDetail104;
+	}
+
+
+
+	public static MessageDetail customInfo(){
+		TypeIdentifierEnum typeIdentifierEnum = TypeIdentifierEnum.shortFloatingPointTelemetry;
+		//SQ=0 length =1
+		int sq = 1;
+		boolean isContinuous = sq == 0 ? false : true;
+		// 接收序号
+		short accept = 1;
+		// 发送序号
+		short send = 4;
+		byte[] control = Iec104Util.getIcontrol(accept, send);
+		// 传输原因
+		short transferReason = 0x01;
+		// true:1 ; false : 0
+		boolean isTest = false;
+		// true:0 false;1
+		boolean isPN = true;
+		short terminalAddress = 1;
+		int messageAddress = 100;
+		// 老板限定词
+		QualifiersEnum qualifiers = QualifiersEnum.qualityQualifiers;
+		List<MessageInfo> messages = new ArrayList<>();
+		MessageInfo message = new MessageInfo();
+		message.setMessageAddress(2);
+		message.setMessageInfos(new byte[] {0x12, 0x34});
+		messages.add(message);
+
+		MessageInfo message2 = new MessageInfo();
+		message.setMessageAddress(3);
+		message2.setMessageInfos(new byte[] {0x56, 0x78});
+		messages.add(message2);
+
+
+		MessageDetail ruleDetail104 = new MessageDetail(control, typeIdentifierEnum, isContinuous, isTest, isPN, transferReason,
+				terminalAddress, messageAddress, messages, null, qualifiers);
+		return ruleDetail104;
+	}
+
+
+
+}

+ 78 - 0
platform-iec/src/test/java/com/ydl/iec/util/ByteUtilTest.java

@@ -0,0 +1,78 @@
+package com.ydl.iec.util;
+
+import org.junit.Test;
+
+import java.util.Date;
+
+import static org.junit.Assert.fail;
+
+public class ByteUtilTest {
+
+	@Test
+	public void testIntToByteArray() {
+		byte[] bytes = ByteUtil.intToByteArray(60);
+		System.err.println(bytes[0] + " " + bytes[1] + " " + bytes[2] + " " + bytes[3] + " ");
+		System.err.println(Integer.toBinaryString(60));
+	}
+
+	@Test
+	public void testShortToByteArray() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testByteArrayToInt() {
+		byte[] bytes = new byte[4];
+		bytes[0] = 0; // 0000 0111 
+		bytes[1] = 104;
+		bytes[2] = 0;
+		bytes[3] = 0;
+		int a = ByteUtil.byteArrayToInt(bytes);
+		System.err.println(Integer.toBinaryString(a));
+		
+		int values =  (bytes[0] & 0xFF) << 24;
+		System.err.println(values);
+		System.err.println(Integer.toBinaryString(values));
+		
+	}
+
+	@Test
+	public void testByteArrayToShort() {
+		int a = 1 << 1;
+		System.err.print(a);
+	}
+
+	@Test
+	public void testListToBytes() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testDate2HByte() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testByte2HDate() {
+//		byte[] datebytes = new byte[] {0x41, 0x00, 0x33, 0x33, 0x47, 0x42, 0x00};
+		byte[] datebytes = new byte[] {0x41, 0x00, 0x33, 0x33, 0x47, 0x42, 0x00};
+		Date date = ByteUtil.byte2Hdate(datebytes);
+		System.err.print(date.getTime());
+	}
+
+	@Test
+	public void testMain() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testByteArrayToHexString() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testByteArray2HexString() {
+		fail("Not yet implemented");
+	}
+
+}

+ 161 - 0
platform-iec/src/test/java/com/ydl/iec/util/RuleDetail104utilTest.java

@@ -0,0 +1,161 @@
+package com.ydl.iec.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * 
+* @ClassName: RuleDetail104utilTest2  
+* @Description: TODO 
+* @author YDL 
+* @date 2020年5月13日
+ */
+public class RuleDetail104utilTest {
+
+	@Test
+	public void testGetIcontrol() {
+		short accept = 0;
+		short send = 0;
+		byte[] control = Iec104Util.getIcontrol((short) accept, (short) send);
+		assertEquals("发送序号不相等", accept, Iec104Util.getAccept(control));
+		assertEquals("接收序号不相等", send, Iec104Util.getSend(control));
+		
+		
+		accept = 1;
+		send = 4;
+		control = Iec104Util.getIcontrol((short) accept, (short) send);
+		assertEquals("发送序号不相等", accept, Iec104Util.getAccept(control));
+		assertEquals("接收序号不相等", send, Iec104Util.getSend(control));
+		
+		
+		send = 32767;
+		accept = send;
+		control = Iec104Util.getIcontrol((short) accept, (short) send);
+		assertEquals("发送序号不相等", accept, Iec104Util.getAccept(control));
+		assertEquals("接收序号不相等", send, Iec104Util.getSend(control));
+	}
+
+
+	@Test
+	public void testGetScontrol() {
+		short accept = 0;
+		byte[] control = Iec104Util.getScontrol(accept);
+		assertEquals("接收序号不相等", accept, Iec104Util.getAccept(control));
+		
+		
+		short getSsontrol = 32767;
+		control = Iec104Util.getScontrol(getSsontrol);
+		
+		
+		control = new byte[] {0x01, 0x00, 0x0A, 0x00 };
+		
+		assertEquals("接收序不相等", getSsontrol, Iec104Util.getAccept(control));
+	}
+	
+	
+	
+	
+
+	@Test
+	public void testGetSend() {
+		fail("Not yet implemented");
+	}
+	@Test
+	public void testGetUcontrol() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testGetControl() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testIntToMessageAddress() {
+	}
+	
+	
+
+	@Test
+	public void testMessageAddressToInt() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testSetChanged() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testGetChangedQualifiers() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testSetMeaageAttribute() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testGetTransferReasonByte() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testIsYes() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testIsTets() {
+		fail("Not yet implemented");
+	}
+
+	@Test
+	public void testGetTransferReasonShort() {
+		// 传输原因
+		short transferReason = 6;
+		// true:1 ; false : 0
+		boolean isTest = false;
+		// true:0 false;1
+		boolean isPN = true;
+		byte[] values = ByteUtil.shortToByteArray(Iec104Util.getTransferReasonShort(isTest, isPN, transferReason));
+		assertEquals(Iec104Util.getTransferReasonShort(values), transferReason);
+		assertEquals(Iec104Util.isYes(values), isPN);
+		assertEquals(Iec104Util.isTets(values), isTest);
+		
+		/*******************************************/
+		// true:1 ; false : 0
+		isTest = true;
+		// true:0 false;1
+		isPN = false;
+		values = ByteUtil.shortToByteArray(Iec104Util.getTransferReasonShort(isTest, isPN, transferReason));
+		assertEquals(Iec104Util.getTransferReasonShort(values), transferReason);
+		assertEquals(Iec104Util.isYes(values), isPN);
+		assertEquals(Iec104Util.isTets(values), isTest);
+		
+		
+		/*******************************************/
+		transferReason = 32;
+		// true:1 ; false : 0
+		isTest = true;
+		// true:0 false;1
+		isPN = false;
+		values = ByteUtil.shortToByteArray(Iec104Util.getTransferReasonShort(isTest, isPN, transferReason));
+		assertEquals(Iec104Util.getTransferReasonShort(values), transferReason);
+		assertEquals(Iec104Util.isYes(values), isPN);
+		assertEquals(Iec104Util.isTets(values), isTest);
+	}
+	
+	@Test
+	public void testGetTerminalAddressByte() {
+		short terminalAddress = 1;
+		byte[] terminalAddressByte =  Iec104Util.getTerminalAddressByte(terminalAddress);
+		short newTerminalAddress =  Iec104Util.getTerminalAddressShort(terminalAddressByte);
+		assertEquals("终端地址解析错误", terminalAddress, newTerminalAddress);
+	}
+
+
+}