JLibModbus 사용법
1. 개요
공식 github:: https://github.com/kochedykov/jlibmodbus
Java에서 사용할 수 있는 모드 버스로 Master와 Slave의 모드버스를 구성할 수 있습니다.
Modbus는 RS-232(시리얼) 또는 TCP의 프로토콜 형태로 구현할 수 있는데, 위 라이브러리는 TCP 프로토콜을 사용하는 라이브러리입니다.
위 라이브러리에서 지원하는 Modbus Function은 아래와 같습니다.
* 0x01 - Read Coils
* 0x02 - Read Discrete Inputs
* 0x03 - Read Holding Registers
* 0x04 - Read Input Registers
* 0x05 - Write Single Coil
* 0x06 - Write Single Register
* 0x07 - Read Exception Status(serial line only)
* 0x08 - Diagnostics(serial line only)
* 0x0B - Get Comm Event Counter(serial line only)
* 0x0C - Get Comm Event Log(serial line only)
* 0x0F - Write Multiple Coils
* 0x10 - Write Multiple Registers
* 0x11 - Report Slave Id(serial line only)
* 0x14 - Read File Record
* 0x15 - Write File Record
* 0x16 - Mask Write Register
* 0x17 - Read Write Multiple Registers
* 0x18 - Read Fifo Queue
* 0x2B - Encapsulated Interface Tansport(Read Device Identification interface, (0x2B / 0x0E))
2. MasterTCP 구성 코드
Master 측에서는 Slave(Server)에 접속할 수 있는 정보(IP, Port)들을 TcpParameter 객체에 정의해둡니다.
후에 ModbusMaster 객체를 팩토리를 통해 초기화합니다.
Slave의 Address는 slaveId이고, 접속할 가상의 Slave의 주소는 1로 설정해둡니다.
Offset은 조회할 주소의 시작 주소입니다. 총 10개의 주소가 생성되었다면 0번째의 주소부터 조회를 한다는 의미이고, 그 밑에 있는 quantity 변수는 0번째부터 순서대로 10번째의 주소를 조회한다는 의미입니다.
Register는 int[] 의 return 값을 가지고, coils은 boolean[]의 return 값을 가집니다.
public class SimpleMasterTCP {
static public void main(String[] args) {
try {
TcpParameters tcpParameters = new TcpParameters();
//tcp parameters have already set by default as in example
tcpParameters.setHost(InetAddress.getLocalHost());
tcpParameters.setKeepAlive(true);
tcpParameters.setPort(Modbus.TCP_PORT);
//if you would like to set connection parameters separately,
// you should use another method: createModbusMasterTCP(String host, int port, boolean keepAlive);
ModbusMaster m = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
Modbus.setAutoIncrementTransactionId(true);
int slaveId = 1;
int offset = 0;
int quantity = 10;
try {
// since 1.2.8
if (!m.isConnected()) {
m.connect();
}
// at next string we receive ten registers from a slave with id of 1 at offset of 0.
int[] registerValues = m.readHoldingRegisters(slaveId, offset, quantity);
for (int value : registerValues) {
System.out.println("Address: " + offset++ + ", Value: " + value);
}
// also since 1.2.8.4 you can create your own request and process it with the master
offset = 0;
ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest();
request.setServerAddress(1);
request.setStartAddress(offset);
request.setTransactionId(0);
ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) m.processRequest(request);
// you can get either int[] containing register values or byte[] containing raw bytes.
for (int value : response.getRegisters()) {
System.out.println("Address: " + offset++ + ", Value: " + value);
}
} catch (ModbusProtocolException e) {
e.printStackTrace();
} catch (ModbusNumberException e) {
e.printStackTrace();
} catch (ModbusIOException e) {
e.printStackTrace();
} finally {
try {
m.disconnect();
} catch (ModbusIOException e) {
e.printStackTrace();
}
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. SlaveTCP 구성 코드
Slave 측에서는 Socket을 활용해 Master가 조회할 수 있는 Server(Ip, Port)를 열고, Master측에서 특정 이벤트(writeSingleCoil, ReadSingleHoldingRegister 등)을 요청할 때 Slave에서 처리할 이벤트의 내용을 정의하는 이벤트 핸들러를 생성하여 DataHolder에 등록해둡니다.
Slave는 가상의 Slave(테스트용 시뮬레이터)이므로 해당 모드버스의 Server Address(여기서는 1)를 정의하고, Coil과 Register의 주소와 값을 정의합니다.
아래 코드에서는 HoldingRegister 객체를 생성할 때의 인자를 10으로 두었는데, 이는 총 10개의 Register에서 10개의 주소를 생성해서 사용한다는 의미이고, hr.set(10, 12345)는 10번째 주소의 Register값을 12345로 정의한다는 의미입니다.
위와 같은 형태로 주소와 값을 임의로 정의하고 slave 객체에서 master의 연결과 응답을 기다리는 스레드를 구현한 형태로 되어있습니다.
public class SimpleSlaveTCP {
static public void main(String[] argv) {
try {
final ModbusSlave slave;
TcpParameters tcpParameters = new TcpParameters();
tcpParameters.setHost(InetAddress.getLocalHost());
tcpParameters.setKeepAlive(true);
tcpParameters.setPort(Modbus.TCP_PORT);
slave = ModbusSlaveFactory.createModbusSlaveTCP(tcpParameters);
Modbus.setLogLevel(Modbus.LogLevel.LEVEL_DEBUG);
MyOwnDataHolder dh = new MyOwnDataHolder();
dh.addEventListener(new ModbusEventListener() {
@Override
public void onWriteToSingleCoil(int address, boolean value) {
System.out.print("onWriteToSingleCoil: address " + address + ", value " + value);
}
@Override
public void onWriteToMultipleCoils(int address, int quantity, boolean[] values) {
System.out.print("onWriteToMultipleCoils: address " + address + ", quantity " + quantity);
}
@Override
public void onWriteToSingleHoldingRegister(int address, int value) {
System.out.print("onWriteToSingleHoldingRegister: address " + address + ", value " + value);
}
@Override
public void onWriteToMultipleHoldingRegisters(int address, int quantity, int[] values) {
System.out.print("onWriteToMultipleHoldingRegisters: address " + address + ", quantity " + quantity);
}
});
slave.setDataHolder(dh);
ModbusHoldingRegisters hr = new ModbusHoldingRegisters(10);
hr.set(0, 12345);
slave.getDataHolder().setHoldingRegisters(hr);
slave.setServerAddress(1);
/*
* using master-branch it should be #slave.open();
*/
slave.listen();
/*
* since 1.2.8
*/
if (slave.isListening()) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
synchronized (slave) {
slave.notifyAll();
}
}
});
synchronized (slave) {
slave.wait();
}
/*
* using master-branch it should be #slave.close();
*/
slave.shutdown();
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
}
public interface ModbusEventListener {
void onWriteToSingleCoil(int address, boolean value);
void onWriteToMultipleCoils(int address, int quantity, boolean[] values);
void onWriteToSingleHoldingRegister(int address, int value);
void onWriteToMultipleHoldingRegisters(int address, int quantity, int[] values);
}
public static class MyOwnDataHolder extends DataHolder {
final List<ModbusEventListener> modbusEventListenerList = new ArrayList<ModbusEventListener>();
public MyOwnDataHolder() {
// you can place the initialization code here
/*
something like that:
setHoldingRegisters(new SimpleHoldingRegisters(10));
setCoils(new Coils(128));
...
etc.
*/
}
public void addEventListener(ModbusEventListener listener) {
modbusEventListenerList.add(listener);
}
public boolean removeEventListener(ModbusEventListener listener) {
return modbusEventListenerList.remove(listener);
}
@Override
public void writeHoldingRegister(int offset, int value) throws IllegalDataAddressException, IllegalDataValueException {
for (ModbusEventListener l : modbusEventListenerList) {
l.onWriteToSingleHoldingRegister(offset, value);
}
super.writeHoldingRegister(offset, value);
}
@Override
public void writeHoldingRegisterRange(int offset, int[] range) throws IllegalDataAddressException, IllegalDataValueException {
for (ModbusEventListener l : modbusEventListenerList) {
l.onWriteToMultipleHoldingRegisters(offset, range.length, range);
}
super.writeHoldingRegisterRange(offset, range);
}
@Override
public void writeCoil(int offset, boolean value) throws IllegalDataAddressException, IllegalDataValueException {
for (ModbusEventListener l : modbusEventListenerList) {
l.onWriteToSingleCoil(offset, value);
}
super.writeCoil(offset, value);
}
@Override
public void writeCoilRange(int offset, boolean[] range) throws IllegalDataAddressException, IllegalDataValueException {
for (ModbusEventListener l : modbusEventListenerList) {
l.onWriteToMultipleCoils(offset, range.length, range);
}
super.writeCoilRange(offset, range);
}
}
}
'Java > 순수 Java' 카테고리의 다른 글
[Java] Birt Project 사용방법 (0) | 2021.11.04 |
---|---|
[Java] Lombok Annotation 정리 (0) | 2020.09.07 |
[Java] Lombok Setter Custom (0) | 2020.09.07 |
[Java] Lombok 설치 방법 (0) | 2020.09.07 |
[Java] 자바 코드 실행시간 측정 방법 (1) | 2020.05.27 |