CZT
2023-09-16 7e6e59aa162776a0ab37cfbc0c6c79d14100eb69
增加DL/T645多功能电表协议
已修改5个文件
已添加8个文件
585 ■■■■■ 文件已修改
igds-core/src/main/java/com/ld/igds/io/constant/ProtocolEnum.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-es/src/main/java/com/ld/igds/es/service/HEsService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-es/src/main/java/com/ld/igds/es/view/EsData.view.xml 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-protocol-es/pom.xml 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/RemoteEsServiceImpl.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/analysis/AnalysisService.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/builder/CommandBuild.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/client/ClientHandler.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/client/Dlt645ClientEngine.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/package-info.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/util/Dlt645Utils.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-web/pom.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
igds-core/src/main/java/com/ld/igds/io/constant/ProtocolEnum.java
@@ -32,6 +32,7 @@
    TCP_BHZH_VERB_V2("TCP_BHZH_VERB_V2", "邦海控制柜智能有线版V2"),
    TCP_BHZH_INOUT_V1("TCP_BHZH_INOUT_V1", "邦海智能出入库控制器协议"),
    TCP_BEIBO_GRAIN_V1("TCP_BEIBO_GRAIN_V1", "贝博粮情分机服务端协议"),
    TCP_ES_DLT645_V1("TCP_ES_DLT645_V1", "DL/T 645多功能电表协议"),
    FZZY_OPENAPI_HTTP("FZZY_OPENAPI_HTTP", "风正致远API-HTTP协议"),
    TCP_MODBUS("TCP_MODBUS", "Modbus-TCP协议");
igds-es/src/main/java/com/ld/igds/es/service/HEsService.java
@@ -75,7 +75,7 @@
            
            Date end = (Date) param.get("end");
            if(null != end){
                start =  DateUtil.getNextZero(end);
                end =  DateUtil.getNextZero(end);
                hql += " and updateTime <:end ";
                args.put("end", end);
            }
igds-es/src/main/java/com/ld/igds/es/view/EsData.view.xml
@@ -255,11 +255,6 @@
          <Property name="property">deviceName</Property>
          <Property name="align">center</Property>
        </DataColumn>
        <DataColumn name="updateTime">
          <Property name="property">updateTime</Property>
          <Property name="align">center</Property>
          <Property name="width">130</Property>
        </DataColumn>
        <ColumnGroup>
          <Property name="caption">三相电压</Property>
          <DataColumn name="ua">
@@ -296,21 +291,11 @@
            <Property name="caption">C相</Property>
          </DataColumn>
        </ColumnGroup>
        <DataColumn name="f">
          <Property name="property">f</Property>
          <Property name="align">center</Property>
          <Property name="width">60</Property>
        </DataColumn>
        <ColumnGroup>
          <Property name="caption">电能(电表值)</Property>
          <DataColumn name="ep">
            <Property name="property">ep</Property>
            <Property name="caption">有功</Property>
            <Property name="align">center</Property>
          </DataColumn>
          <DataColumn name="eq">
            <Property name="property">eq</Property>
            <Property name="caption">无功</Property>
            <Property name="align">center</Property>
          </DataColumn>
          <DataColumn name="es">
@@ -319,10 +304,10 @@
            <Property name="caption">总电能</Property>
          </DataColumn>
        </ColumnGroup>
        <DataColumn name="esInc">
          <Property name="property">esInc</Property>
        <DataColumn name="updateTime">
          <Property name="property">updateTime</Property>
          <Property name="align">center</Property>
          <Property name="caption">小时用能</Property>
          <Property name="width">150</Property>
        </DataColumn>
      </DataGrid>
    </Container>
igds-protocol-es/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.ld.igds</groupId>
    <artifactId>igds-protocol-es</artifactId>
    <version>4.0.0-RELEASE</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <igds.version>4.0.0-RELEASE</igds.version>
    </properties>
    <dependencies>
        <!-- å¼•入义务核心包 -->
        <dependency>
            <groupId>com.ld.igds</groupId>
            <artifactId>igds-core</artifactId>
            <version>${igds.version}</version>
        </dependency>
        <!-- å¼•入能耗包 -->
        <dependency>
            <groupId>com.ld.igds</groupId>
            <artifactId>igds-es</artifactId>
            <version>${igds.version}</version>
        </dependency>
        <!-- å¼•å…¥IO包 -->
        <dependency>
            <groupId>com.ld.base.io</groupId>
            <artifactId>base-io-netty</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArguments>
                        <extdirs>src\main\webapp\WEB-INF\lib</extdirs>
                    </compilerArguments>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/RemoteEsServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package com.ld.igds.protocol.es.dlt645;
import com.ld.igds.constant.RedisConst;
import com.ld.igds.io.RemoteEsService;
import com.ld.igds.io.constant.OrderRespEnum;
import com.ld.igds.io.constant.ProtocolEnum;
import com.ld.igds.io.request.BaseRequest;
import com.ld.igds.io.response.BaseResponse;
import com.ld.igds.io.response.GrainResponse;
import com.ld.igds.protocol.es.dlt645.builder.CommandBuild;
import com.ld.igds.protocol.es.dlt645.client.Dlt645ClientEngine;
import com.ld.igds.util.BytesUtil;
import com.ld.igds.util.RedisUtil;
import com.ld.io.api.InvokeResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * å½“前协议实现,能耗协议
 *
 * @author czt
 */
@Slf4j
@Component(RemoteEsServiceImpl.BEAN_ID)
public class RemoteEsServiceImpl implements RemoteEsService {
    public static final String BEAN_ID = "dlt645.remoteEsServiceImpl";
    @Autowired
    private RedisUtil redisUtil;
    @Override
    public String getProtocol() {
        return ProtocolEnum.TCP_ES_DLT645_V1.getCode();
    }
    @Override
    public BaseResponse checkEs(BaseRequest request) {
        try {
            log.info("DL/T645多功能电表分机开始检测,分机{}的IP={},端口={}",request.getSerId(),request.getIp(),request.getPort());
            Dlt645ClientEngine clientEngine = new Dlt645ClientEngine(request.getIp(), request.getPort());
            clientEngine.start();
            Thread.sleep(1000);
            //采集命令存入缓存,解析时分辨是哪个仓的能耗,时效5分钟
            String key = RedisConst.buildKey(request.getCompanyId(), "ES-DLT645", request.getSerId());
            redisUtil.set(key, request.getDepotId(), 60*5);
            //生成命令-查询总电能的命令
            String hexStr = CommandBuild.getInstance().getMsgCheck(request.getSerId());
            InvokeResult result = clientEngine.send(BytesUtil.hexStrToBytes(hexStr));
            log.info("result=" +result.getMessage());
            // å°è£…返回信息
            if (InvokeResult.SUCCESS == result) {
                return new GrainResponse(OrderRespEnum.ORDER_SUCCESS.getCode(),
                        "能耗命令发送成功!");
            } else {
                log.error("平台------>>>>能耗分机:能耗检测-失败{}", result.getMessage());
                return new GrainResponse(OrderRespEnum.ORDER_ERROR.getCode(),
                        "命令发送异常:" + result.getMessage());
            }
        } catch (Exception e) {
            log.error("能耗检测异常:{}", e);
            return new BaseResponse(OrderRespEnum.ORDER_ERROR.getCode(), "后端异常:" + e.getMessage());
        }
    }
}
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/analysis/AnalysisService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
package com.ld.igds.protocol.es.dlt645.analysis;
import com.ld.igds.common.CoreSerService;
import com.ld.igds.constant.RedisConst;
import com.ld.igds.es.dto.EsData;
import com.ld.igds.es.service.CoreEsService;
import com.ld.igds.models.DeviceSer;
import com.ld.igds.protocol.es.dlt645.util.Dlt645Utils;
import com.ld.igds.util.ContextUtil;
import com.ld.igds.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
 * DLT645电表协议解析
 *
 * @author czt
 */
@Slf4j
@Component(AnalysisService.BEAN_ID)
public class AnalysisService {
    public static final String BEAN_ID = "dlt645.analysisService";
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private CoreSerService coreSerService;
    @Autowired
    private CoreEsService esService;
    /**
     *
     * @param result
     */
    public void analysis(String result){
        log.info("能耗分机------->>平台:信息报文={}", result);
        if(!result.startsWith(Dlt645Utils.MSG_START)){
            log.error("能耗分机------->>平台,解析能耗失败:报文起始符错误,不解析");
            return;
        }
        //去除起始符
        result = result.substring(8);
        //获取校验位
        String oldCheck = result.substring(result.length() - 4, result.length() - 2);
        String check = Dlt645Utils.makeCheck(result.substring(0, result.length() - 4));
        if(!check.equals(oldCheck)){
            log.error("能耗分机------->>平台,解析能耗失败:校验位不对,不解析");
            return;
        }
        analysisGrain(result);
    }
    private void analysisGrain(String result) {
        try {
            //获取能耗分机地址
            String serId = Dlt645Utils.addrToString(result.substring(2, 14));
            //根据分机地址获取分机信息
            DeviceSer ser = coreSerService.getCacheSer(ContextUtil.getDefaultCompanyId(), serId);
            if (ser == null) {
                log.error("能耗分机-------->>平台,解析能耗失败,未获取到系统能耗主机配置:" + serId);
                return;
            }
            String key = RedisConst.buildKey(ser.getCompanyId(), "ES-DLT645", ser.getId());
            String depotId = (String) redisUtil.get(key);
            if(null == depotId){
                log.error("能耗分机-------->>平台,解析能耗失败,未获取到仓库信息:" + depotId);
                return;
            }
            //解析总能耗数据
            String dataStr = result.substring(28, 36);
            Double esNum = Dlt645Utils.parseData(dataStr);
            EsData esData = new EsData();
            esData.setDeviceName("电表");
            esData.setCompanyId(ser.getCompanyId());
            esData.setDepotId(depotId);
            esData.setUpdateTime(new Date());
            esData.setEp(esNum);
            esData.setEs(esNum);
            log.info("DLT645电表----->>>平台:能耗数据解析完成-仓库={}", esData.getDepotId());
            esService.saveAndUpdateInc(esData);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/builder/CommandBuild.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package com.ld.igds.protocol.es.dlt645.builder;
import com.ld.igds.protocol.es.dlt645.util.Dlt645Utils;
/**
 * ç”µè¡¨å‘½ä»¤ç”Ÿæˆ
 *
 * @author czt
 */
public class CommandBuild {
    private final static CommandBuild instance = new CommandBuild();
    private CommandBuild() {
    }
    public static CommandBuild getInstance() {
        return instance;
    }
    /**
     * èƒ½è€—采集命令
     *
     * @param esAddr ç”µè¡¨åœ°å€
     * @return
     */
    public static String getMsgCheck(String esAddr) {
        //获取命令
        StringBuffer sb = new StringBuffer();
        //帧起始符
        sb.append("68");
        //地域域,反向拼接
        sb.append(Dlt645Utils.addrToString(esAddr));
        //帧起始符
        sb.append("68");
        //控制码及
        sb.append("11");
        //数据域长度
        sb.append("04");
        //数据域
        sb.append("33333433");
        //校验码
        String checkNum = Dlt645Utils.makeCheck(sb.toString());
        sb.append(checkNum);
        StringBuffer sb0 = new StringBuffer();
        //起始符
        sb0.append(Dlt645Utils.MSG_START);
        sb0.append(sb);
        //结尾符
        sb0.append(Dlt645Utils.MSG_END);
        return sb0.toString();
    }
}
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/client/ClientHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package com.ld.igds.protocol.es.dlt645.client;
import com.ld.igds.protocol.es.dlt645.analysis.AnalysisService;
import com.ld.igds.util.BytesUtil;
import com.ld.igds.util.SpringUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.springframework.stereotype.Component;
import java.net.InetSocketAddress;
/**
 * Handles a client-side channel.
 */
public class ClientHandler extends SimpleChannelInboundHandler<Object> {
    private AnalysisService analysisService;
    private final InternalLogger log = InternalLoggerFactory.getInstance(this.getClass());
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel()
                .remoteAddress();
        log.info("电表服务器成功连接,IP={},port={}", insocket.getAddress()
                .getHostAddress(), insocket.getPort());
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel()
                .remoteAddress();
        log.info("电表服务器断开连接,IP={},port={}", insocket.getAddress(),insocket.getPort());
    }
    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        byte[] bytes = (byte[]) msg;
        String result = BytesUtil.bytesToString(bytes);
        log.info("电表服务器分机返回信息=" + result);
//        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        //解析
        //调用解析接口开始解析
        if (null == analysisService) {
            analysisService = (AnalysisService) SpringUtil.getBean(AnalysisService.BEAN_ID);
        }
        analysisService.analysis(result);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.close();
    }
}
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/client/Dlt645ClientEngine.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,98 @@
package com.ld.igds.protocol.es.dlt645.client;
import com.ld.io.api.InvokeResult;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.oio.OioSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import lombok.extern.slf4j.Slf4j;
/**
 * DL/T 645-2007多功能电表
 *
 * @author czt
 */
@Slf4j
public class Dlt645ClientEngine implements Runnable {
    private String host;
    private int port;
    public Channel channel;
    public Dlt645ClientEngine(String host, int port) {
        this.host = host;
        this.port = port;
    }
    public void start() {
        Thread thread = new Thread(this);
        thread.start();
    }
    @Override
    public void run() {
        try {
            startRun();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void startRun() throws Exception {
        EventLoopGroup group = new OioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        // b.option(ChannelOption.SO_KEEPALIVE, true);
        b.group(group).channel(OioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch)
                            throws Exception {
                        ChannelPipeline p = ch.pipeline();
                        // å­—符串解码 å’Œ ç¼–码
                        p.addLast("decoder", new ByteArrayDecoder());
                        p.addLast("encoder", new ByteArrayEncoder());
                        // è‡ªå·±çš„逻辑Handler
                        p.addLast("handler", new ClientHandler());
                    }
                });
        // å‘起异步连接请求,绑定连接端口和host信息
        ChannelFuture channelFuture = b.connect(host, port);
        this.channel = channelFuture.channel();
        // channelFuture.channel().closeFuture().sync();
        channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture arg0) throws Exception {
                if (channelFuture.isSuccess()) {
                    log.info("-----连接电表服务器成功,IP={},PORT={}", host, port);
                } else {
                    log.info("-----连接电表服务器失败,IP={},PORT={}", host, port);
                    channelFuture.cause().printStackTrace();
                    group.shutdownGracefully(); // å…³é—­çº¿ç¨‹ç»„
                }
            }
        });
    }
    public InvokeResult send(byte[] array) throws InterruptedException {
        if (null == channel) {
            return InvokeResult.SOCKET_NOT_CREATE;
        }
        if (!channel.isActive()) {
            return InvokeResult.CHANNEL_CLOSED;
        }
        channel.writeAndFlush(Unpooled.copiedBuffer(array)).sync();
        return InvokeResult.SUCCESS;
    }
    public Channel getChannel() {
        return channel;
    }
}
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/package-info.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
/**
 * @Desc: DL/T 645多功能电表
 * @author: czt
 */
package com.ld.igds.protocol.bhzn;
igds-protocol-es/src/main/java/com/ld/igds/protocol/es/dlt645/util/Dlt645Utils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,88 @@
package com.ld.igds.protocol.es.dlt645.util;
/**
 * å·¥å…·ç±»
 *
 * @author czt
 */
public class Dlt645Utils {
    public static String MSG_START = "FEFEFEFE"; //起始符
    public static String MSG_END = "16"; //结尾符
    public static void main(String[] args) {
        String s = makeCheck("6857141222000068110433333433");
        System.out.println(s);
    }
    /**
     * å°†6个字节的地址域,每字节2位 BCD码,转换为12位10进制数
     */
    public static String addrToString(String str) {
        byte[] bytes = stringToBytes(str);
        return String.format("%02x%02x%02x%02x%02x%02x", bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
    }
    /**
     * DLT645校验码
     */
    public static String makeCheck(String data) {
        if (data == null || data.equals("")) {
            return "";
        }
        int total = 0;
        int len = data.length();
        int num = 0;
        while (num < len) {
            String s = data.substring(num, num + 2);
            total += Integer.parseInt(s, 16);
            num = num + 2;
        }
        /**
         * ç”¨256求余最大是255,即16进制的FF
         */
        int mod = total % 256;
        String hex = Integer.toHexString(mod);
        len = hex.length();
        // å¦‚果不够校验位的长度,补0,这里用的是两位校验
        if (len < 2) {
            hex = "0" + hex;
        }
        return hex.toUpperCase();
    }
    /**
     * å°†String类型转换成byte数组
     */
    public static byte[] stringToBytes(String str) {
        if (str == null || str.equals("")) {
            return null;
        }
        byte[] bytes = new byte[str.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            String subStr = str.substring(i * 2, i * 2 + 2);
            bytes[i] = (byte) Integer.parseInt(subStr, 16);
        }
        return bytes;
    }
    /**
     * è§£æžè¿”回的具体数据
     */
    public static Double parseData(String dataStr) {
        byte[] data = stringToBytes(dataStr);
        // æ¯ä¸ªå­—节-33H处理
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) (data[i] - 0x33);
        }
        StringBuilder str = new StringBuilder();
        // BCD码转换成10进制
        for (byte b : data) {
            str.insert(0, String.format("%02x", b));
        }
        return Double.parseDouble(str.toString()) * 0.01;
    }
}
igds-web/pom.xml
@@ -222,6 +222,23 @@
            </exclusions>
        </dependency>-->
        <!--  ç§æœ‰åè®®-DLT645电表协议-->
        <dependency>
            <groupId>com.ld.igds</groupId>
            <artifactId>igds-protocol-es</artifactId>
            <version>${igds.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>log4j</artifactId>
                    <groupId>log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--  ç§æœ‰åè®®-邦海智能
        <dependency>
            <groupId>com.ld.igds</groupId>
pom.xml
@@ -43,6 +43,8 @@
        <module>igds-protocol-zldz</module>
        <!--贝博粮情协议-->
        <module>igds-protocol-beibo</module>
        <!--电表-->
        <module>igds-protocol-es</module>
        <!--物联网拓展设备包-->
        <module>igds-protocol-iot</module>
@@ -52,6 +54,7 @@
        <!--modbus-tcp-->
        <module>igds-protocol-modbus</module>
        <!--    æ‰‹æœºåŒ…    -->
        <module>igds-api-phone</module>
        <!--    æŽ¥å£åŒ…,包括三维接口    -->