浏览代码

104协议处理完善

songwenbin 2 年之前
父节点
当前提交
6650a74ce4

+ 5 - 4
gddlly/src/main/java/com/gyee/edge/gddlly/config/Iec104Config.java

@@ -16,14 +16,15 @@ public class Iec104Config {
     private short port = 8019;
 
     //轮询发送时间
-    private int pollingInterval = 1000;
-
+    private int pollingInterval = 3000;
 
     //接收到帧的数量到该值就要发一个确认帧
     private short frameAmountMax;
 
-    //终端地址
-    private short terminnalAddress;
+    private int frameAiMax = 16;
+    private int frameDiMax = 22;
+
+
 
     @Override
     public String toString() {

+ 63 - 19
gddlly/src/main/java/com/gyee/edge/gddlly/config/PointService.java

@@ -8,6 +8,7 @@ import org.springframework.stereotype.Service;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -19,17 +20,18 @@ public class PointService {
     private Database db;
 
     //外层: key = 分组号(iec104 asduAddress), Value = 该分组下所有点的map结构
-    //内层:key = 点名, value = point对象
-    private Map<Integer, Map<String, Point>> aiMap;
+    private Map<Integer, ArrayList<Point>> aiMap;
+    private Map<Integer, ArrayList<Point>> diMap;
 
-    private Map<Integer, Map<String, Point>> diMap;
+    private Map<Integer, ArrayList<String>> aiKeyMap;
+    private Map<Integer, ArrayList<String>> diKeyMap;
 
     public void initPointMap() {
         aiMap = loadAiPointMap();
         diMap = loadDiPointMap();
     }
 
-    public Map<Integer, Map<String, Point>> getAiMap(){
+    public Map<Integer, ArrayList<Point>> getAiList(){
 
         if (aiMap == null || aiMap.isEmpty()){
             aiMap = loadAiPointMap();
@@ -38,12 +40,15 @@ public class PointService {
         return aiMap;
     }
 
-    private Map<Integer, Map<String, Point>> loadAiPointMap() {
-        Map<Integer, Map<String, Point>> map = new HashMap<>();
+    private Map<Integer, ArrayList<Point>> loadAiPointMap() {
+        Map<Integer, ArrayList<Point>> map = new HashMap<>();
+        aiKeyMap = new HashMap<>();
+
         try {
             log.info("加载AI测点......");
             Connection conn = db.getConnection();
-            String sql = "select * from point where pointtype = 'AI'";
+            //确保地址连续
+            String sql = "select * from point where pointtype = 'AI' order by publicaddr, pointaddr asc";
             log.info("执行SQL:" + sql);
             PreparedStatement ps = conn.prepareStatement(sql);
             ResultSet rs = ps.executeQuery();
@@ -58,12 +63,16 @@ public class PointService {
                 point.setOrg(rs.getString("org"));
 
                 if (map.containsKey(point.getPublicAddr()) == false) {
-                    Map<String, Point> subMap = new HashMap<>();
+                    ArrayList<Point> subMap = new ArrayList<>();
                     map.put((Integer)point.getPublicAddr(), subMap);
+                    ArrayList<String> lstStr = new ArrayList<>();
+                    aiKeyMap.put((Integer)point.getPublicAddr(), lstStr);
                 }
-                Map<String, Point> subMap = map.get(point.getPublicAddr());
-                if (subMap.containsKey(point.getPoint()) == false)
-                    subMap.put(point.getPoint(), point);
+                ArrayList<Point> subMap = map.get(point.getPublicAddr());
+                subMap.add(point);
+                ArrayList<String> lstStr = aiKeyMap.get(point.getPublicAddr());
+                lstStr.add(point.getPoint());
+
             }
         } catch (Exception e) {
             log.info("sqlite查询失败",e);
@@ -74,7 +83,7 @@ public class PointService {
         return map;
     }
 
-    public Map<Integer, Map<String, Point>> getDiMap(){
+    public Map<Integer, ArrayList<Point>> getDiList(){
         if (diMap == null || diMap.isEmpty()){
             diMap = loadDiPointMap();
         }
@@ -82,12 +91,13 @@ public class PointService {
         return diMap;
     }
 
-    private Map<Integer, Map<String, Point>> loadDiPointMap() {
-        Map<Integer, Map<String, Point>> map = new HashMap<>();
+    private Map<Integer, ArrayList<Point>> loadDiPointMap() {
+        Map<Integer, ArrayList<Point>> map = new HashMap<>();
+        diKeyMap = new HashMap<>();
         try {
             log.info("加载DI测点......");
             Connection conn = db.getConnection();
-            String sql = "select * from point where pointtype = 'DI'";
+            String sql = "select * from point where pointtype = 'DI' order by publicaddr, pointaddr";
             log.info("执行SQL:" + sql);
             PreparedStatement ps = conn.prepareStatement(sql);
             ResultSet rs = ps.executeQuery();
@@ -102,12 +112,15 @@ public class PointService {
                 point.setOrg(rs.getString("org"));
 
                 if (map.containsKey(point.getPublicAddr()) == false) {
-                    Map<String, Point> subMap = new HashMap<>();
+                    ArrayList<Point> subMap = new ArrayList<>();
                     map.put((Integer)point.getPublicAddr(), subMap);
+                    ArrayList<String> lstStr = new ArrayList<>();
+                    diKeyMap.put((Integer)point.getPublicAddr(), lstStr);
                 }
-                Map<String, Point> subMap = map.get(point.getPublicAddr());
-                if (subMap.containsKey(point.getPoint()) == false)
-                    subMap.put(point.getPoint(), point);
+                ArrayList<Point> subMap = map.get(point.getPublicAddr());
+                subMap.add(point);
+                ArrayList<String> lstStr = diKeyMap.get(point.getPublicAddr());
+                lstStr.add(point.getPoint());
             }
         } catch (Exception e) {
             log.info("sqlite查询失败",e);
@@ -119,5 +132,36 @@ public class PointService {
     }
 
 
+    public ArrayList<Point> getAiList(int addr) {
+        if (aiMap == null || aiMap.isEmpty()){
+            aiMap = loadAiPointMap();
+        }
+
+        return aiMap.get(addr);
+    }
+
+    public ArrayList<Point> getDiList(int addr) {
+        if (diMap == null || diMap.isEmpty()){
+            diMap = loadDiPointMap();
+        }
+
+        return diMap.get(addr);
+    }
+
+    public ArrayList<String> getAiKeys(int addr) {
+        if (aiKeyMap == null || aiKeyMap.isEmpty()){
+            loadAiPointMap();
+        }
+
+        return aiKeyMap.get(addr);
+    }
+
+    public ArrayList<String> getDiKeys(int addr) {
+        if (diKeyMap == null || diKeyMap.isEmpty()){
+            loadDiPointMap();
+        }
+
+        return diKeyMap.get(addr);
+    }
 
 }

+ 14 - 14
gddlly/src/main/java/com/gyee/edge/gddlly/iec104/Iec104Message.java

@@ -1,5 +1,6 @@
 package com.gyee.edge.gddlly.iec104;
 
+import com.gyee.edge.gddlly.config.Point;
 import com.gyee.edge.gddlly.iec104.protocol.QualifiersEnum;
 import com.gyee.edge.gddlly.iec104.protocol.TypeIdentifierEnum;
 import lombok.Data;
@@ -23,12 +24,17 @@ public class Iec104Message {
 	 * APDU 长度1个字节
 	 */
 	private int apduLength = 0;
+
 	
 	/**
 	 * 控制域 四个字节
 	 */
 	private byte[] control;
-	
+
+	private short acceptSeq;
+
+	private short sendSeq;
+
 	
 	/**
 	 * 类型标识 1字节
@@ -45,9 +51,9 @@ public class Iec104Message {
 	private boolean  isContinuous;
 
 	/**
-	 * 消息长度
+	 * 消息数量
 	 */
-	private int measgLength;
+	private int infosize;
 	/**
 	 * 传输原因 两个字节
 	 */
@@ -67,7 +73,10 @@ public class Iec104Message {
 	 * 消息结构
 	 */
 	private List<MessageInfo> messages;
-	
+
+	//直接关联到点表
+	private List<Point> aiPointList;
+	private List<Point> diPointList;
 	
 	/**
 	 * 判断是否有消息元素
@@ -118,7 +127,7 @@ public class Iec104Message {
 		this.control = control;
 		this.typeIdentifier = typeIdentifierEnum;
 		this.isContinuous = sq;
-		this.measgLength = messages.size();
+		this.infosize = messages.size();
 		this.transferReason = transferReason; // Iec104Util.getTransferReasonShort(isTest, isPn, transferReason);
 		this.messages = messages;
 		this.terminalAddress = terminalAddress;
@@ -139,14 +148,5 @@ public class Iec104Message {
 		this.messages = new ArrayList<>();
 	}
 
-	public short getAcceptSeq() {
-		//从控制域3、4提取接受报文的序列号
-		return Iec104Util.getAccept(this.control);
-	}
 
-	public short getSendSeq() {
-		//从控制域1、2提取发送报文的序列号
-		return Iec104Util.getSend(this.control);
-	}
-	
 }

+ 84 - 25
gddlly/src/main/java/com/gyee/edge/gddlly/iec104/Iec104Session.java

@@ -1,33 +1,27 @@
 package com.gyee.edge.gddlly.iec104;
 
+import com.gyee.edge.gddlly.config.Iec104Config;
 import com.gyee.edge.gddlly.config.Point;
 import com.gyee.edge.gddlly.config.PointService;
-import com.gyee.edge.gddlly.exception.InitErrorException;
 import com.gyee.edge.gddlly.iec104.protocol.BasicInstruction104;
 import com.gyee.edge.gddlly.iec104.protocol.TransferReason;
 import com.gyee.edge.gddlly.iec104.protocol.TypeIdentifierEnum;
 import com.gyee.edge.gddlly.iec104.protocol.UControlEnum;
 import com.gyee.edge.gddlly.redis.RedisDataService;
 import com.gyee.edge.gddlly.utils.SpringContextUtil;
-import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.*;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.nio.NioSocketChannel;
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.GenericFutureListener;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
 
 import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.Map;
+import java.util.List;
 
 @Slf4j
 public class Iec104Session {
     private String clientIp;
     private int clientPort;
-    private short asduAddress;
+    private short publicAddress;
     private Date connectTime;
     private SessionState state;
     private Date lastSessionTime;
@@ -39,9 +33,10 @@ public class Iec104Session {
 
     private PointService pointService = SpringContextUtil.getBean(PointService.class);
     private RedisDataService redisService = SpringContextUtil.getBean(RedisDataService.class);
+    private Iec104Config iec104Config = SpringContextUtil.getBean(Iec104Config.class);
 
-    private Map<String, Point> aiMap;
-    private Map<String, Point> diMap;
+    private ArrayList<Point> aiList;
+    private ArrayList<Point> diList;
 
     public Iec104Session(ChannelHandlerContext ctx) {
         channelHandlerContext = ctx;
@@ -88,9 +83,9 @@ public class Iec104Session {
                 // 总召唤命令
                 if (TransferReason.ACTIVATE == TransferReason.valueOf(request.getTransferReason())) {
                     state = SessionState.CALL_ALL;
-                    asduAddress = request.getTerminalAddress();
-                    aiMap = pointService.getAiMap().get((int)asduAddress);
-                    diMap = pointService.getDiMap().get((int)asduAddress);
+                    publicAddress = request.getTerminalAddress();
+                    aiList = pointService.getAiList().get((int)publicAddress);
+                    diList = pointService.getDiList().get((int)publicAddress);
                     //todo: 启动全量传输
                     this.start();
                     acceptSeq = request.getAcceptSeq();
@@ -139,22 +134,35 @@ public class Iec104Session {
                     while(isRunning) {
                         //todo: 从redis读取数据,发送到客户端
                         //1、从redis读取数据,填充aiMap、diMap中,point的value,consumed,lastUpdateTime;
-                        aiMap = redisService.getData(aiMap);
-                        diMap = redisService.getData(diMap);
-
-                        if (state == SessionState.CALL_ALL) {
-                            //2、如果SessionState.CALL_ALL, 全量传输
-
-                        } else if (state == SessionState.CALL_ALL_END) {
-                            //3、如果SessionState.CALL_ALL_END,变化传输
+                        if (redisService.readRedisData(publicAddress,"AI")) {
+                            if (state == SessionState.CALL_ALL) {
+                                //2、如果SessionState.CALL_ALL, 全量传输遥测点
+                                sendPoints(aiList, true, "AI");
+                            } else if (state == SessionState.CALL_ALL_END) {
+                                //3、如果SessionState.CALL_ALL_END,变化传输遥测点
+                                sendPoints(aiList, false, "AI");;
+                            } else {
+                                break;
+                            }
+                        }
 
-                        } else {
-                            break;
+                        if (redisService.readRedisData(publicAddress,"DI")) {
+                            if (state == SessionState.CALL_ALL) {
+                                //2、如果SessionState.CALL_ALL, 全量传输遥测点
+                                sendPoints(diList, true, "DI");
+                            } else if (state == SessionState.CALL_ALL_END) {
+                                //3、如果SessionState.CALL_ALL_END,变化传输遥测点
+                                sendPoints(diList, false, "DI");
+                            } else {
+                                break;
+                            }
                         }
 
-                        Thread.sleep(3000);
+                        Thread.sleep(iec104Config.getPollingInterval());
                     }
                 } catch (Exception e) {
+                    //todo: 异常处理
+                    log.error(e.getMessage());
 
                 } finally {
                     isRunning = false;
@@ -163,4 +171,55 @@ public class Iec104Session {
         });
     }
 
+    private boolean sendPoints(ArrayList<Point> allPoints, boolean isContinous, String pointType) {
+        List<Point> pointList = new ArrayList<>();
+        Iec104Message msg104 = new Iec104Message();
+        msg104.setAcceptSeq(acceptSeq);
+        msg104.setTransferReason((short)20);
+        //每个消息内测点的数量
+        int maxPointsPerMessage = 16;
+        if ("AI".equals(pointType.toUpperCase())) {
+            msg104.setTypeIdentifier(TypeIdentifierEnum.shortFloatingPointTelemetry); //M_ME_TF_1
+            maxPointsPerMessage = iec104Config.getFrameAiMax();
+        } else {
+            msg104.setTypeIdentifier(TypeIdentifierEnum.onePointTimeTeleindication); //M_SP_TB_1
+            maxPointsPerMessage = iec104Config.getFrameDiMax();
+        }
+        //todo: 设置VSQ,可变结构描述限定词
+        msg104.setContinuous(isContinous);
+        msg104.setTerminalAddress(publicAddress);
+        //消息地址为信息体中第一个元素的地址
+        int firstMsgAddr = allPoints.get(0).getPointAddr();
+        msg104.setMessageAddress(firstMsgAddr);
+
+        for(int i=0;i<allPoints.size();i++) {
+            Point point = allPoints.get(i);
+            if (pointList.size() >= maxPointsPerMessage) {
+                msg104.setAiPointList(pointList);
+                msg104.setSendSeq(sendSeq++);
+                msg104.setInfosize(pointList.size());
+                channelHandlerContext.writeAndFlush(msg104);
+                //todo:考虑实现确认S帧
+
+                //重置point列表缓存,消息地址,其它内容不变
+                if (i < aiList.size()-1) {
+                    pointList = new ArrayList<>();
+                    firstMsgAddr = allPoints.get(i+1).getPointAddr();
+                    msg104.setMessageAddress(firstMsgAddr);
+                }
+            }
+            pointList.add(aiList.get(i));
+            point.setConsumed(true);
+        }
+
+        if (pointList.size() > 0) {
+            msg104.setAiPointList(pointList);
+            msg104.setSendSeq(sendSeq++);
+            msg104.setInfosize(pointList.size());
+            channelHandlerContext.writeAndFlush(msg104);
+        }
+
+        return true;
+    }
+
 }

+ 15 - 5
gddlly/src/main/java/com/gyee/edge/gddlly/iec104/Iec104Util.java

@@ -1,7 +1,6 @@
 package com.gyee.edge.gddlly.iec104;
 
 
-import com.gyee.edge.gddlly.iec104.protocol.TransferReason;
 import com.gyee.edge.gddlly.iec104.protocol.TypeIdentifierEnum;
 import com.gyee.edge.gddlly.iec104.protocol.UControlEnum;
 import com.gyee.edge.gddlly.utils.ByteUtil;
@@ -141,11 +140,11 @@ public class Iec104Util {
 	 * @param msg104
 	 * @param byteItem
 	 */
-	public static void setChanged(Iec104Message msg104, byte byteItem) {
+	public static void setVSQ(Iec104Message msg104, byte byteItem) {
 		// 第一位是 0 则是有序的
 		msg104.setContinuous((byteItem & 0x80) == 0 ? false : true);
 		// 先将第一位数置零 然后转换成int
-		msg104.setMeasgLength(byteItem & (byte) 0x7F);
+		msg104.setInfosize(byteItem & (byte) 0x7F);
 	}
 	    
 	/**	
@@ -155,12 +154,21 @@ public class Iec104Util {
 	 */
 	public static byte getChangedQualifiers(Iec104Message msg104) {
 		// 将长度转换成 byte
-		byte changedQualifiers = (byte) msg104.getMeasgLength();
+		byte changedQualifiers = (byte) msg104.getInfosize();
 		// 判断SQ 置   isContinuous false SQ = 0;否则 SQ =1 ,  同时将SQ置 设置在 可变限定词的 D7位置
 		int sq = msg104.isContinuous() ?  0x80 : 0;
 		changedQualifiers = (byte) (sq | changedQualifiers);
 		return changedQualifiers;
-	} 
+	}
+
+	public static byte getVSQ(boolean isContinuous, int infosize) {
+		// 将长度转换成 byte
+		byte vsq = (byte)infosize;
+		// 判断SQ 置   isContinuous false SQ = 0;否则 SQ =1 ,  同时将SQ置 设置在 可变限定词的 D7位置
+		int sq = isContinuous ?  0x80 : 0;
+		vsq = (byte) (sq | vsq);
+		return vsq;
+	}
 	
 	
 	public static void setMeaageAttribute(Iec104Message msg104) {
@@ -249,4 +257,6 @@ public class Iec104Util {
 		 value += (terminalAddress[1] & 0xFF) << 8;
 		 return value;
 	}
+
+
 }

+ 7 - 4
gddlly/src/main/java/com/gyee/edge/gddlly/iec104/builder/Decoder104.java

@@ -29,7 +29,10 @@ public class Decoder104 {
 		int index = 0;
 		msg104.setStart(bytes[index++]);
 		msg104.setApduLength(bytes[index++] & 0xFF);
-		msg104.setControl(ByteUtil.getByte(bytes, index, 4));
+		byte[] control = ByteUtil.getByte(bytes, index, 4);
+		msg104.setControl(control);
+		msg104.setAcceptSeq(Iec104Util.getAccept(control));
+		msg104.setSendSeq(Iec104Util.getSend(control));
 		index += 4;
 		if (msg104.getApduLength() <= 4) {
 			msg104.setMessages(new ArrayList<>());
@@ -39,7 +42,7 @@ public class Decoder104 {
 		// 下面是返回ASDU的结构
 		msg104.setTypeIdentifier(TypeIdentifierEnum.getTypeIdentifierEnum(bytes[index++]));
 		// 添加可变结构限定词
-		Iec104Util.setChanged(msg104, bytes[index++]);
+		Iec104Util.setVSQ(msg104, bytes[index++]);
 		msg104.setTransferReason(ByteUtil.byteToShortLittle(ByteUtil.getByte(bytes, index, 2)));
 		index += 2;
 		// 
@@ -93,7 +96,7 @@ public class Decoder104 {
 			// 获取每个消息的长度
 			int messageLength = getMessageLength(msg104);
 			int messageSize = 0;
-			while (messageSize < msg104.getMeasgLength()) {
+			while (messageSize < msg104.getInfosize()) {
 				MessageInfo messageObj = new MessageInfo();
 				messageObj.setMessageAddress(messageAddress);
 				byte[] messageInfos = ByteUtil.getByte(bytes, mesageIndex, messageLength);
@@ -133,7 +136,7 @@ public class Decoder104 {
 		// 获取每个消息的长度
 		int messageLength = getMessageLength(msg104);
 		int messageSize = 0;
-		while (messageSize < msg104.getMeasgLength()) {
+		while (messageSize < msg104.getInfosize()) {
 			MessageInfo messageObj = new MessageInfo();
 			// 消息地址
 			messageObj.setMessageAddress(Iec104Util.messageAddressToInt(ByteUtil.getByte(bytes, mesageIndex, 3)));

+ 55 - 33
gddlly/src/main/java/com/gyee/edge/gddlly/iec104/builder/Encoder104.java

@@ -1,33 +1,42 @@
 package com.gyee.edge.gddlly.iec104.builder;
 
 
+import com.gyee.edge.gddlly.config.Point;
 import com.gyee.edge.gddlly.iec104.Iec104Util;
 import com.gyee.edge.gddlly.iec104.Iec104Message;
 import com.gyee.edge.gddlly.iec104.MessageInfo;
 import com.gyee.edge.gddlly.utils.ByteUtil;
+import lombok.extern.slf4j.Slf4j;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
+@Slf4j
 public class Encoder104 {
 	
 	public static byte[] encoder(Iec104Message msg104) throws IOException {
-		Iec104Util.setMeaageAttribute(msg104);
-		ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-		bytes.write(msg104.getStart());
-		byte[]  apduBytes = getApduBytes(msg104);
-		int messageLen =  apduBytes.length;
-		msg104.setApduLength(messageLen);
-		bytes.write((byte) messageLen);
-		bytes.write(apduBytes);
+		try {
+			Iec104Util.setMeaageAttribute(msg104);
+			ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+			bytes.write(msg104.getStart());
+			byte[]  apduBytes = getApduBytes(msg104);
+			int messageLen =  apduBytes.length;
+			msg104.setApduLength(messageLen);
+			bytes.write((byte) messageLen);
+			bytes.write(apduBytes);
+
+			return bytes.toByteArray();
+		} catch (Exception e) {
+			log.error(e.getMessage(), e.fillInStackTrace());
+			throw new IOException();
+		}
 
-		return bytes.toByteArray();
 	}
 	
 	private static byte[] getApduBytes(Iec104Message msg104) throws IOException {
 		ByteArrayOutputStream bOutput = new ByteArrayOutputStream();
 		//  控制域
-		bOutput.write(msg104.getControl());
+		bOutput.write(Iec104Util.getIcontrol(msg104.getAcceptSeq(),msg104.getSendSeq()));
 		if (msg104.getTypeIdentifier() == null) {
 			// U帧或者S帧
 			return bOutput.toByteArray();
@@ -35,40 +44,53 @@ public class Encoder104 {
 		// 类型标识
 		bOutput.write((byte) msg104.getTypeIdentifier().getValue());
 		// 可变结构限定词
-		bOutput.write(Iec104Util.getChangedQualifiers(msg104));
+		//bOutput.write(Iec104Util.getChangedQualifiers(msg104));
+		bOutput.write(Iec104Util.getVSQ(msg104.isContinuous(), msg104.getInfosize()));
 		// 传输原因
 		bOutput.write(ByteUtil.shortToByteLittle(msg104.getTransferReason()));
 		// 终端地址
 		bOutput.write((Iec104Util.getTerminalAddressByte(msg104.getTerminalAddress())));
 //		如果是是连续的则数据地址 只需要在开头写以后的数据单元就不需要再写了
 		if (msg104.isContinuous()) {
+			//写首测点地址,取三个字节
 			bOutput.write(Iec104Util.intToMessageAddress(msg104.getMessageAddress()));
-			// 地址只取三个字节
-			if (msg104.isMessage()) {
-				for (MessageInfo msg104Message : msg104.getMessages()) {
-					bOutput.write(msg104Message.getMessageInfos());
+			//遍历测点列表,
+			if (msg104.getAiPointList() != null && msg104.getAiPointList().size() > 0) {
+				//按照M_ME_TF_1序列化数据
+				for(Point point: msg104.getAiPointList()) {
+					bOutput.write(ByteUtil.float2Bytes((float) point.getValue()));
+					bOutput.write(ByteUtil.date2Hbyte(point.getLastUpdateTime()));
+					//todo: 考虑是否写品质描述词?
 				}
-			} 
-			if (msg104.isQualifiers()) {
-				bOutput.write(msg104.getQualifiers().getValue());
-			} 
-			if (msg104.isTimeScaleExit()) {
-				bOutput.write(ByteUtil.date2Hbyte(msg104.getTimeScale()));
-			} 
+			} else if (msg104.getDiPointList() != null && msg104.getDiPointList().size() > 0) {
+				//按照M_SP_TB_1序列化数据
+				for(Point point: msg104.getDiPointList()) {
+					bOutput.write(point.getValue() == 0 ? (byte)0 : (byte)1);
+					bOutput.write(ByteUtil.date2Hbyte(point.getLastUpdateTime()));
+					//todo: 考虑是否写品质描述词?
+				}
+			}
+
 		} else {
-			for (MessageInfo msg104Message : msg104.getMessages()) {
-				bOutput.write(Iec104Util.intToMessageAddress(msg104Message.getMessageAddress()));
-				if (msg104.isMessage()) {
-					bOutput.write(msg104Message.getMessageInfos());
-				} 
-				if (msg104.isQualifiers()) {
-					bOutput.write(msg104Message.getQualifiers().getValue());
-				} 
-				if (msg104.isTimeScaleExit()) {
-					bOutput.write(ByteUtil.date2Hbyte(msg104Message.getTimeScale()));
-				} 
+			if (msg104.getAiPointList() != null && msg104.getAiPointList().size() > 0) {
+				//按照M_ME_TF_1序列化数据,带地址
+				for(Point point: msg104.getAiPointList()) {
+					bOutput.write(Iec104Util.intToMessageAddress(point.getPointAddr()));
+					bOutput.write(ByteUtil.float2Bytes((float) point.getValue()));
+					bOutput.write(ByteUtil.date2Hbyte(point.getLastUpdateTime()));
+					//todo: 考虑是否写品质描述词?
+				}
+			} else if (msg104.getDiPointList() != null && msg104.getDiPointList().size() > 0) {
+				//按照M_SP_TB_1序列化数据
+				for(Point point: msg104.getDiPointList()) {
+					bOutput.write(Iec104Util.intToMessageAddress(point.getPointAddr()));
+					bOutput.write(point.getValue() == 0 ? (byte)0 : (byte)1);
+					bOutput.write(ByteUtil.date2Hbyte(point.getLastUpdateTime()));
+					//todo: 考虑是否写品质描述词?
+				}
 			}
 		}
+
 		return bOutput.toByteArray();
 	}
 

+ 63 - 1
gddlly/src/main/java/com/gyee/edge/gddlly/redis/RedisDataService.java

@@ -1,6 +1,7 @@
 package com.gyee.edge.gddlly.redis;
 
 import com.gyee.edge.gddlly.config.Point;
+import com.gyee.edge.gddlly.config.PointService;
 import com.gyee.edge.gddlly.utils.RedisUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -16,6 +17,9 @@ public class RedisDataService {
     @Autowired
     private RedisUtil redisUtil;
 
+    @Autowired
+    private PointService pointService;
+
     public Map<String, Point> getData(Map<String, Point> map){
 
         List<String> points = new ArrayList<>(map.keySet());
@@ -45,7 +49,7 @@ public class RedisDataService {
                 }
                // point.setConsumed(point.getValue() == value ? true : false);
 
-                map.replace(points.get(i), point);
+                //map.replace(points.get(i), point);
             }
         } catch (Exception e){
             log.error(e.getMessage());
@@ -56,4 +60,62 @@ public class RedisDataService {
         return map;
     }
 
+    public boolean readRedisData(int addr,String typeStr){
+        List<String> keys;
+        List<Point>  points;
+        try {
+            if (typeStr.toUpperCase().equals("AI")) {
+                keys = pointService.getAiKeys(addr);
+                points = pointService.getAiList(addr);
+            } else {
+                keys = pointService.getDiKeys(addr);
+                points = pointService.getDiList(addr);
+            }
+
+            if (keys == null || points == null || keys.isEmpty() ||
+                keys.size() != points.size() ) {
+                log.error("地址错误! publicaddr = " + addr);
+                return false;
+            }
+
+            List<String> vals = (List<String>)this.redisUtil.getmAll(keys);
+            if (vals == null || vals.isEmpty() || vals.size() != keys.size()) {
+                log.error("读取redis错误!publicaddr = " + addr);
+                return false;
+            }
+
+            for (int i = 0; i < keys.size(); i++){
+                Point point = points.get(i);
+                String val = vals.get(i);
+                if (point == null || !StringUtils.hasText(val))
+                    continue;
+
+                String[] vls = val.split(":");
+                if (vls.length != 2)
+                    continue;
+
+                String ts = vls[0].trim();
+                String vs = vls[1].trim();
+
+                long time = Long.valueOf(ts.substring(ts.indexOf("\"") + 1, ts.length() - 1));
+                double value = Double.valueOf(vs.substring(1, vs.lastIndexOf("\"")));
+
+                point.setLastUpdateTime(new Date(time));
+                if (point.getValue() != value ) {
+                    point.setConsumed(false);
+                    point.setValue(value);
+                }
+                // point.setConsumed(point.getValue() == value ? true : false);
+
+                //map.replace(points.get(i), point);
+            }
+        } catch (Exception e){
+            log.error(e.getMessage());
+            e.printStackTrace();
+            return false;
+        }
+
+        return true;
+    }
+
 }

+ 23 - 1
gddlly/src/main/java/com/gyee/edge/gddlly/utils/ByteUtil.java

@@ -11,7 +11,29 @@ import java.util.Date;
 * @author sun 
  */
 public class ByteUtil {
-	
+
+    public static byte[] float2Bytes(float value) {
+
+        int tempVal = Float.floatToIntBits(value);
+        byte[] result = new byte[4];
+
+        result[0] = (byte) tempVal;
+        result[1] = (byte) (tempVal >> 8);
+        result[2] = (byte) (tempVal >> 16);
+        result[3] = (byte) (tempVal >> 24);
+
+        return result;
+    }
+
+    public static float bytes2Float(byte[] bytes) {
+
+        return Float.intBitsToFloat((bytes[0] & 0xff) |
+                ((bytes[1] & 0xff) << 8) |
+                ((bytes[2] & 0xff) << 16) |
+                ((bytes[3] & 0xff) << 24));
+
+    }
+
 	/**
 	 * 
 	* @Title: intToByteArray