MODBUS报文负数优化处理代码(补码,反码) java

白色玫瑰 程序猿

时间: 2023-07-11 阅读: 1 字数:6807

{}
MODBUS报文负数优化处理代码(补码,反码) java前言写此文档的原因参考资料简洁代码实现方式实现代码核心转码代码转换的工具类:结束 前言 写此文档的原因 今天弄MDOBUS报文处理,因为以前弄个,但处理数据时用了...

MODBUS报文负数优化处理代码(补码,反码) java

<a href="#_2">前言</a>

<a href="#_3">写此文档的原因</a>    <a href="#_6">参考资料</a>    <a href="#_12">简洁代码实现方式</a>    <a href="#_22">实现代码</a>    
 
  <a href="#_25">核心转码代码</a>      <a href="#_68">转换的工具类:</a>      <a href="#_169">结束</a>            

前言

写此文档的原因

今天弄MDOBUS报文处理,因为以前弄个,但处理数据时用了各种 If 和 for,代码极度不简洁也不高效,所以单独查询更简洁更高效的处理方法,但各种资料中,解决原理和解决方式都有,但具体代码描述和实际操作并没有说明,所以单独记录了此文档。

参考资料

以下文章需取重点同步观看,每个都只讲了部分内容 代码中所使用的运算符及原因: <a href="https://blog.csdn.net/lsr40/article/details/78020039">点击查看原帖</a>. 为什么转换会这么麻烦及原因: <a href="https://www.cnblogs.com/yichunguo/p/12410360.html">点击查看原帖</a>. 原码、反码、补码和符号介绍: <a href="https://blog.csdn.net/weixin_30271447/article/details/114954744?utm_term=bytejava%E5%8F%8D%E7%A0%81&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduweb~default-5-114954744&spm=3001.4430">点击查看原帖</a>.

简洁代码实现方式

代码想简洁高效,首先就要理清操作原因:

在MODBUS中,正数取值和负数取值是不一样的; 如果直接把负数当成正数转码进行数据转换,转换时会把符号位当数据进行了转换; MODBUS报文分为状态位(简单理解为false和true)和数据位数据; MODBUS数据位分为数值和字符(ACISS),数值又分为正数和小数,此处不讨论小数 MODBUS一个字节(byte)只能保存0~255(也可以说是-128到127)的数字 当数值超过一个byte时,需继续增加byte传递数据,此时需区分高位和低位(可以简单理解为高位值为2时代表两个满值低位) 有部分厂家,高位和低位具体是哪个不一定,有的前面是高位,有的后面是高位,需确认区分

实现代码

实现的代码分两部分,一个是简洁的,少的关键转码(主要是负数),还有一个是地城的byte工具类,但是我不需要使用它的jar包,所以把方法抽了出来成为工具类使用

核心转码代码

此代码为转码核心,先区分正负值在根据符号去处理

/**
    * MODBUS转码
    * (不支持小数转码)
    *
    * @param byte1 待转码数据(高位)
    * @param byte2 待转码数据(低位)
    */
   public Double byteToInt(byte byte1,byte byte2){
      //高位在前,低位在后
      //byte[] bs1 = new byte[]{(byte) 0xFF,(byte) 0x3D};      //测试数据:负数   -195
      //byte[] bs1 = new byte[]{(byte) 0x00,(byte) 0xC3};      //测试数据:正数   195
      int number = 0;
      //1、取出高位符号
      if(byte1<0){
         //负数先高位左移8位和低位拼接,再取反
         number =~(byte1<<8 | byte2);
         //补码并赋值符号位
         number =-(number+1);
      }else{
         //正数直接转成16位字符(主要为拼接字符串)
         String s = XmByteUtils.toHexAscii(new byte[]{byte1,byte2});
         //直接把16进制字符转int
         number = Integer.parseInt(s, 16);
      }
      //把已取出的数据做额外处理
      return (double) number / 10;      //具体数据处理
   }

代码解析: 原始数据转换例子(数值随便写的,仅是案例): byte1:10000101 byte2:00011101

转换解释及案例 byte1<<8:把高位数据左移8位,给低位数据合并留出空间 (例:10000101 00000000) byte1<<8|byte2:把高位数据和低位数据合并(例:10000101 00011101) ~(byte1<<8 | byte2):反码:(例:01111010 11100010) -(number+1):补码就是加一,因为符号位丢失,再给他加上(例:11111010 11100011)

转换的工具类:

具体干什么就不细讲了,我也是把底层原码考出的,当做工具类使用

package com.system.xiaoma.util;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

public class XmByteUtils {

   /**
    * MODBUS报文字符串(不带空格)转 byte可发送的HEX格式
    * @param s MODBUS报文字符串
    */
   public static byte[] fromHexAscii(String s) throws NumberFormatException {
      try {
         int len = s.length();
         if ((len % 2) != 0)
            throw new NumberFormatException("Hex ascii must be exactly two digits per byte.");

         int out_len = len / 2;
         byte[] out = new byte[out_len];
         int i = 0;
         StringReader sr = new StringReader(s);
         while (i < out_len) {
            int val = (16 * fromHexDigit(sr.read())) + fromHexDigit(sr.read());
            out[i++] = (byte) val;
         }
         return out;
      } catch (IOException e) {
         throw new InternalError("IOException reading from StringReader?!?!");
      }
   }

   /**
    *
    * @param c 需发送命令的16位数值转换
    * @return
    * @throws NumberFormatException
    */
   private static int fromHexDigit(int c) throws NumberFormatException {
      if (c >= 0x30 &amp;&amp; c < 0x3A)
         return c - 0x30;
      else if (c >= 0x41 &amp;&amp; c < 0x47)
         return c - 0x37;
      else if (c >= 0x61 &amp;&amp; c < 0x67)
         return c - 0x57;
      else
         throw new NumberFormatException('\'' + c + "' is not a valid hexadecimal digit.");
   }

   /**
    * byte数组转十六位字符串(不带空格)
    * @param bytes 待转换的byte数组
    */
   public static String toHexAscii(byte[] bytes) {
      int len = bytes.length;
      StringWriter sw = new StringWriter(len * 2);
      for (int i = 0; i < len; ++i)
         addHexAscii(bytes[i], sw);
      return sw.toString();
   }

   /**
    * byte转16位并拼接
    * @param b    数组
    * @param sw   字符串
    */
   static void addHexAscii(byte b, StringWriter sw) {
      int ub = unsignedPromote(b);
      int h1 = ub / 16;
      int h2 = ub % 16;
      sw.write(toHexDigit(h1));
      sw.write(toHexDigit(h2));
   }

   /**
    * byte取补位数据
    * @param b 数组
    * @return
    */
   public static int unsignedPromote(byte b) {
      return b &amp; 0xff;
   }

   /* note: we do no arg. checking, because    */
   /* we only ever call this from addHexAscii() */
   /* above, and we are sure the args are okay  */
   private static char toHexDigit(int h) {
      char out;
      if (h <= 9) out = (char) (h + 0x30);
      else out = (char) (h + 0x37);
      //System.err.println(h + ": " + out);
      return out;
   }
}

结束

如果上面代码有BUG或更方便的写法请在评论告知,我会及时更新的,后续使用中我也会根据使用情况更新代码,谢谢!

晋级处理方式来啦:<a href="https://blog.csdn.net/qq_35449424/article/details/128010990">点击查看新内容</a>

原文地址:https://blog.csdn.net/qq_35449424/article/details/119822313?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168904451816800186548539%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168904451816800186548539&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-24-119822313-null-null.142^v88^control_2,239^v2^insert_chatgpt&utm_term=java%E4%BC%98%E5%8C%96

本文章网址:https://www.sjxi.cn/detil/9635ef88b1a742ea9b1314df26ff6e4c

最新评论

当前未登陆哦
登陆后才可评论哦

湘ICP备2021009447号

×

(穷逼博主)在线接单

QQ: 1164453243

邮箱: abcdsjx@126.com

前端项目代做
前后端分离
Python 爬虫脚本
Java 后台开发
各种脚本编写
服务器搭建
个人博客搭建
Web 应用开发
Chrome 插件编写
Bug 修复