工业废水信息接入Java

废水信息采用Modbus-RTU 协议

运维给过来的数据

废水 00.00.00.00:18800 心跳包 F020010F (HEX 60s一次)
0 通道  COD 量程0-1000
1 通道   PH   量程0-14
2 通道  氨氮  量程0-50

采集: 01 03 00 00 00 06    05 CB 
		                   CRC16
返回: 01 03 06  30 84    95 7B     0D BB     CD AD
               通道0     通道1     通道2      CRC16
公式:
COD=   (30 84)            /      65535         *     1000           = 189.51
    (通道0十进制)12420        (int最大值)        (最大量程)

科普一下,RTU和TCP区别是 RTU带有校验,没有协议报文头,用串口通信

我们是服务端,接受到心跳信息,回采集报文

上代码

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);

        String strReq = Util.bytesToHexString(req);
        log.info("收到的请求是:" + strReq);


        if(strReq.equals(HEARTBEAT)){
            ByteBuf echo = Unpooled.copiedBuffer(Util.hexString2Bytes("01030000000305CB"));
            ctx.writeAndFlush(echo);
        }else{
            if(strReq.length() == 22 ){
                if(CRC16Util.checkCrc(StrUtil.split(strReq, 2))){
                    Integer cod = Integer.parseInt(strReq.substring(6,10),16);
                    Integer ph = Integer.parseInt(strReq.substring(10,14),16);
                    Integer ad = Integer.parseInt(strReq.substring(14,18),16);

                    log.info(new BigDecimal(cod / INT_MAX * MAXIMUM1).setScale(2,BigDecimal.ROUND_DOWN));
                    log.info(new BigDecimal(ph / INT_MAX * MAXIMUM2).setScale(2,BigDecimal.ROUND_DOWN));
                    log.info(new BigDecimal(ad / INT_MAX * MAXIMUM3).setScale(2,BigDecimal.ROUND_DOWN));

                }else{
                    log.error("crc error");
                }
            }else{
                log.info("Invalid message");
            }
        }

        super.channelRead(ctx, msg);
    }

StrUitl使用hutool工具类

CRC16Class代码如下

扫描二维码关注公众号,回复: 14823427 查看本文章

/**
 * ModbusCRC16工具类
 * @author blackteachan
 *
 */
public class CRC16Util {

	/**
	 * ModeBusRTUCRC16校验及java代码
	 * @param msg 无校验位报文,eg:"01 03 00 00 00 01"
	 * @return
	 */
	public static String crc(String msg)
	{
	    String[] info = msg.split(" ");
	    int[] data = new int[info.length];
	    for (int h = 0; h < info.length; h++)
	    {
	        data[h] = Integer.parseInt(info[h], 16);
	    }
	    int[] temdata = new int[data.length + 2];
	    //unsigned char alen = *aStr – 2;   //CRC16只计算前两部分
	    int xda, xdapoly;
	    int i, j, xdabit;
	    xda = 0xFFFF;
	    xdapoly = 0xA001; // (X**16 + X**15 + X**2 + 1)
	    for (i = 0; i < data.length; i++)
	    {
	        xda ^= data[i];
	        for (j = 0; j < 8; j++)
	        {
		        xdabit = (int) (xda & 0x01);
		        xda >>= 1;
		        if (xdabit == 1)
		        {
		          xda ^= xdapoly;
		        }
	        }
	    }
	    System.arraycopy(data, 0, temdata, 0, data.length);
	    temdata[temdata.length - 2] = (int) (xda & 0xFF);
	    temdata[temdata.length - 1] = (int) (xda >> 8);
	    String crcInfo = getHexString(temdata);
	    return insertSpace(crcInfo);
	}

	public static String getHexString(int[] b)  //获取16进制字符串
	{
	    String crcInfo = "";
	    for (int p = 0; p < b.length; p++)
	    {
	      if (b[p] >= 16)
	      {
	        crcInfo += Integer.toHexString(b[p]).toString();
	      }
	      else
	      {
	        crcInfo = crcInfo + "0" + Integer.toHexString(b[p]).toString();
	      }
	    }
	    return crcInfo.toUpperCase();
	}

	//加入空格
	public static String insertSpace(String marStr)
	{
	    StringBuilder sb = new StringBuilder(marStr);
	    for (int i = marStr.length() - 2; i > 0; i -= 2)
	    {
	      sb.insert(i, " ");
	    }
	    String marStrNew = sb.toString();
	    return marStrNew;
	}
	
	/**
	 * 检查校验位
	 * @param arrayCommand 带校验位报文
	 * @return
	 */
	public static boolean checkCrc(String[] arrayCommand) {
		// 带空格原始报文
		String command1 = String.join(" ",arrayCommand);
//		System.out.println("原始报文: " + command1);
		// 原始报文校验位
		String crc1 = (arrayCommand[arrayCommand.length - 2] + 
				arrayCommand[arrayCommand.length - 1]).toUpperCase();
//		System.out.println("原始校验位: " + crc1);
		
		// 带校验报文
		String[] newArrayCommand = Arrays.copyOf(arrayCommand, arrayCommand.length - 2);
		String command2 = String.join(" ", newArrayCommand);
		String newCommand = crc(command2);
//		System.out.println("新的报文: " + newCommand);
		newArrayCommand = newCommand.split(" ");
		// 新校验
		String crc2 = (newArrayCommand[newArrayCommand.length - 2] + 
				newArrayCommand[newArrayCommand.length - 1]).toUpperCase();
//		System.out.println("新的校验位: " + crc2);
		
		return crc1.equals(crc2);
	}

}

猜你喜欢

转载自blog.csdn.net/zjy660358/article/details/124940828