Jamod 实现Modbus 协议的细节

▼魔方 西西 提交于 2020-03-05 00:11:47

本文根据Jamod 的官网介绍进行修改完成的代码http://jamod.sourceforge.net/kb/serial_master_howto.html
原文是英文的,选择重要的部分进行了翻译

1. What is a Slave
  In terms of the Client-Server network computing paradigm, the Slave application is a Server. It has a Listener for receiving an incoming Request from the Master application (which indeed is a Client) and sends a corresponding Response.
  就客户机-服务器这种网络计算模式而言,Slave(从)应用就是服务端,他有一个监听器接受来自主应用(实际上就是一个客户端)的请求并且发送一个响应的回应。
  As described in Understanding the Protocol, each cycle of Request and Response is called a Transaction.Figure 1 shows a simple graphical representation of such a cycle:
  正如在Understanding the Protocol这一节里面描述的,每一个请求和响应的循环被称之为一次事务,图1显示了这种循环的简单的图像表示。
Figure 1: Modbus Transaction
 In case of the serial implementation, the communication can be point-to-point (RS232, EIA422) or on a shared signal cable (EIA485). In both cases there should be only one master, that acquires data from a source (data acquisition), or writes data to a sink (device control).
 在串行通信的情况下,这样的通信可以使点对点的(RS232 EIA422),或者一个共享信号线的EOA485形式。不论是RS232/RIA422还是EIA485 应该只有一个master,这个master可以从一个源上获取数据(数据请求)或者向接收器写去数据(驱动控制)

2. Classes of Interest for the Developer 和开发者相关的类

The motivation for creating jamod was to achieve an intuitive and object oriented implementation of the protocol, in a way, that there is a natural mapping from the domain knowledge (i.e. Modbus protocol) to the abstract class model. The important elements in the description above (What is a Master?) have been highlighted and the following list represents the mapping between them and the classes from jamod that will be needed for a master implementation:
创建Jamod的目的就是为了实现 直观的并且是面向对象的Modbus协议。在Modbus协议的相关知识和抽象类的模型有一个自然映射。一些关键的元素已经在上面的 What is a Master强调过了,下面的列表展示了在 Modbus中的关键元素和Jamod里面类的一个映。

Connection: SerialConnection
Transaction: ModbusSerialTransaction
Request: ModbusRequest (respectively it’s direct known subclass ReadInputRegistersRequest)
Response: ModbusResponse (respectively it’s direct known subclass ReadInputRegistersResponse)
Parameters: SerialParameters

3. 具体的JAVA实现

		/* The important instances of the classes mentioned before */
		//前面提到的需要类的实例
		//SerialConnection 用来打开和串口的连接
    	SerialConnection con = null; //the connection
    	//发送请求和响应请求必须要有事务,所以有下面三个实例
    	ModbusSerialTransaction trans = null; //the transaction
    	ReadCoilsRequest req = null; //the request
    	ReadCoilsResponse res = null; //the response
    	
    	/* Variables for storing the parameters */
    	//存储参数的变量
    		//我们打开串口,肯定需要串口名字,来制定你想打开谁
    	String portname= "COM3"; //the name of the serial port to be used   端口名
    		//
    	int unitid = 2; //the unit identifier we will be talking to    Slave的设备号
    	int ref = 0; //the reference number of the register to read from.  从哪个寄存器地址开始读
		int count = 10; //the count of IR's to read   //从起始的那个寄存器开始一共读几个寄存器
		//2. Set master identifier  设置master的ID 
    	//ModbusCoupler.createModbusCoupler(null); //这句话,我就搞不明白了,API根本没有这函数
    	ModbusCoupler.getReference().setUnitID(1);        //master的ID,虽然没用到,还是设置一下
    	//3. Setup serial parameters   设置串口参数 ,在Jamod里面使用SerialParameters串口参数类
    	SerialParameters params = new SerialParameters();
    	params.setPortName(portname);//端口名
    	params.setBaudRate(9600);//波特率
    	params.setDatabits(8);//数据位
    	params.setParity("None");//校验无
    	params.setStopbits(1);//停止位
    	params.setEncoding("ascii");//编码方式是ASCII码
    	params.setEcho(false);//这个还没研究出来是啥,有啥用
	//4. Open the connection    打开串口 
    
    	//SerialConnection 实现了一个串口的链接,能够在master和slave的实例上使用
         //SerialParameters的实例用来初始化SerialConnection
    	con = new SerialConnection(params); 
    	con.open();
		//5. Prepare a request   准备一个请求
    	req=new ReadCoilsRequest(ref, count);
    	req.setUnitID(unitid);
    	req.setHeadless(); //没有头,我的理解是只有TCP时候才需要头的
    	//6. Prepare a transaction   事务就是处理请求和响应的,你的求情是由事务类调用的
    	// Constructs a new ModbusSerialTransaction instance with a given ModbusRequest to be send 
    	// when the transaction is executed.
    	trans = new ModbusSerialTransaction(con);
    	trans.setRequest(req);
    	//7. Execute the transaction repeat times
    	  trans.execute();
    	  res = (ReadCoilsResponse) trans.getResponse();//对应slave返回的响应
    	  //这里bit数量,需要说一下,在功能代码1时,读1-8个寄存器的值,返回8bit,读大于8个小于等于16 就是16bit,
    	 System.out.println("主人,我读到数据了,一共"+res.getBitCount()+"个bit,快夸我");
    	 BitVector vec=res.getCoils();
    	 for(int m=0;m<vec.size();m++) {
    		 System.out.println(vec.getBit(m));
    	 }
    	 System.out.println("state");
    	 for(int m=0;m<count;m++)
    	 System.out.println("Index="+m+"的state的状态为:"+res.getCoilStatus(m));
    	//8. Close the connection
    	con.close();  

一定要注意功能码和对应的请求类和响应类,看好是1 2 3 4哪一类的功能码
在这里插入图片描述
补充1:
Modbus协议在ASCII传输模式下,消息帧以3A 也就是英文的冒号开始,以回车(0D)和换行(0A)结束。
在ASCII模式下,每个8位的字节被拆分成两个ASCII字符进行发送,比如十六进制0xAF(1010 1111),会被分解成ASCII字符“A”(0100 0001)和”F”(0100 0110)进行发送

Slave 发送TX:3A 30 32 30 31 30 32 37 35 30 30 38 36 0D 0A
3A
30 32 对应十六进制的0X02 slave设备的ID
30 31 对应十六进制的0X01 功能代码
30 32 37 35 30 30 0X027500 0000 ‭0010 0111 0101 0000 0000‬
38 36 LRC校验
0D 0A 结束符

Slave 接受RX:3A 30 32 30 31 30 30 30 30 30 30 30 41 46 33 0D 0A
3A
30 32 对应十六进制的0X02 slave设备的ID
30 31 对应十六进制的0X01 功能代码
30 30 30 30 30 30 30 41 这里最后的41就是java程序要多少个寄存器的值
46 33
0D 0A

补充2:关于ModbusCoupler记录一下自己理解,但是没实践过,
 首先要了解Modbus协议中的过程映像(Process Image),看了看英文的http://jamod.sourceforge.net/kb/processimage.html网址里面的说明,感觉就是说 现在有一个过程,这个过程是连续变化的(比如1+1+1… 一秒加一次),然后有很多的测量函数来测量 这个变化过程的值,很明显这些测量函数必须是肯定是时间的函数比如可以是:f(ti), g(ti), h(ti)
 特别是如果我们有多个“用户”(即相应的控制程序,网络数据获取,可视化等)同时访问数据,则我们要确保这个与时间这个属性。因此,请求不是直接针对相应的I / O模块,而是针对与某个时间点相对应的集合,该集合周期性地存储在内存块中(通常部署某种同步机制以进行顺序访问)。这种存储的过程测量值(或I / O状态)存储集通常称为“过程映像”,因为它表示某个时间点(分别根据我们可以测量的结果)的过程状态。
  Modbus中定义的两种数据类型。Coil是位(bit)变量;Register是整型(Word,即16-bit)变量
  Modbus中,数据可以分为两大类,分别为Coil和Register,每一种数据,根据读写方式的不同,又可细分为两种(只读,读写)。
Modbus四种数据类型:
  Discretes Input    位变量    只读
  Coils          位变量    读写
  Input Registers    16-bit整型   只读
  Holding Registers    16-bit整型   读写
 但是在Jamod中,对Modbus中的四个数据类型就行了抽象,如下

  1. a Digital Input(for a discrete input 对应离散输入)
  2. a Digital Output (for a discrete output or coil 对应离散输出或者coil)
  3. an Input Register (for an input register 对应输入寄存器)
  4. a Register (for a holding register 对应保持寄存器)

上面的几个对应的类分别是DigitalIn DigitalOut InputRegister Register。
 
 还有一个类 ProcessImage表示实际的过程映像,它是先前提供的元素(DigitalIn,DigitalOut,InputRegister和Register)所有实例的集合。
根据Modbus规范,对于没有实际内存限制的虚拟设备,此数据在“内存”中的最简单组织是每种数据类型的单独块。产生的软件模型(ProcessImage和ProcessImageImplementation)如下图所示。

图5:过程映像模型
这里就再说一下ModbusCoupler 这个类,在API中,关于他的说明是implemented following a Singleton pattern, to couple the slave side with a master side or with a device.
At the moment it only provides a reference to the OO model of the process image.
由单例模式实现的,用来将slave端和master端相结合。在某一个确定的时间,仅仅提供一个关于过程映像的面向对象模型的引用。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!