本文根据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显示了这种循环的简单的图像表示。
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
一定要注意功能码和对应的请求类和响应类,看好是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中的四个数据类型就行了抽象,如下
上面的几个对应的类分别是DigitalIn DigitalOut InputRegister Register。
还有一个类 ProcessImage表示实际的过程映像,它是先前提供的元素(DigitalIn,DigitalOut,InputRegister和Register)所有实例的集合。
根据Modbus规范,对于没有实际内存限制的虚拟设备,此数据在“内存”中的最简单组织是每种数据类型的单独块。产生的软件模型(ProcessImage和ProcessImageImplementation)如下图所示。
这里就再说一下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端相结合。在某一个确定的时间,仅仅提供一个关于过程映像的面向对象模型的引用。