๋ชจ๋๋ฒ์ค TCP
๋ชจ๋๋ฒ์ค(Modbus) ํ๋กํ ์ฝ์ PLC์ ๊ฐ์ ์๋ํ ์ ์ด ์ค๋น๊ฐ ํฌํจ๋๋ ์ฐ์ ์์ ๊ณต์์ ์ธ ํ์ค์ ์๋์ง๋ง ๊ฑฐ์ ํ์ค์ฒ๋ผ ์ฌ์ฉ๋๊ณ ์๋ ํต์ ํ๋กํ ์ฝ์ด๋ค. ๋ชจ๋๋ฒ์ค ํ๋กํ ์ฝ์ ๋ํ ์คํ์ Modbus_Messaging_Implementation_Guide_V1_0b.pdf๋ก ๊ณต๊ฐ๋์ด์๋ค. ๋ณธ์ธ์ ์๋์ง ๋ถ์ผ ๋๋ฉ์ธ์์ ์ผํ๊ณ ์๋ ๊ฐ๋ฐ์๋ก ๊ฐ์ข ์๋์ง ๊ด๋ จ ์ค๋น์์๋ ๋ชจ๋๋ฒ์ค ํ๋กํ ์ฝ์ ์ง์ํ๋ ์ ํ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ค๋น๋ค์์ ๋ฐ์ํ๋ ๋ค์ํ ์๊ณ์ด ๋ฐ์ดํฐ๋ค์ ์์งํ๊ธฐ ์ํ์ฌ ๋ชจ๋๋ฒ์ค ํ๋กํ ์ฝ์ ์ฌ์ฉํ๊ณ ์๋ค. ์๋ํ ์ ์ด์ ๋ชฉ์ ์ด ์๋๋ฏ๋ก ์๋ฆฌ์ผ ํต์ ์ด ์๋ SCADA, RTU, PLC์ ๊ฐ์ ์ค๋น์ TCP/IP ๋ฐฉ์์ผ๋ก ํต์ ํ๋ค.
Modbus TCP/IP
๋ชจ๋๋ฒ์ค ํ๋กํ ์ฝ์ ์๋ํ ์ค๋น ์ฐ์ ์์ ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ ์ผ๋ฐ์ ์ธ ์น ๊ฐ๋ฐ์๋ค์ด ๊ฒฝํํ ์ ์๊ฑฐ๋ ์์์ผํ ๋ฒ์ฉ์ ์ธ ํต์ ํ๋กํ ์ฝ์ ์๋๋ค. ๋ชจ๋๋ฒ์ค TCP์์๋ MBAP(MODBUS Application Protocol) ํค๋์ Function Code ๊ทธ๋ฆฌ๊ณ ๋ฐ์ดํฐ ํ๋ ์์ ํ๋๋ก ์ ๋ฌํ๊ฒ ๋๋ฉฐ ์ผ๋ฐ์ ์ธ ์์ผ ํต์ ๊ณผ ๋์ผํ๋ค. ๋ํ, ๋ชจ๋๋ฒ์ค ํ๋กํ ์ฝ์ ๋ง์คํฐ โข ์ฌ๋ ์ด๋ธ ๊ตฌ์กฐ๋ก ๋์ํ๋ฉฐ ๋ง์คํฐ(ํด๋ผ์ด์ธํธ)๋ ์ฌ๋ ์ด๋ธ(์๋ฒ)์์ ์ ์ํ ๋ฉ๋ชจ๋ฆฌ ๋งต ์ ๋ณด๋ฅผ ํ ๋๋ก ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋์ด์๋ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฑฐ๋ ์ํ๋ ๋ช ๋ น์ ์ํํ๋๋ก ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.
MBAP ํค๋
MBAP ํค๋๋ฅผ ๊ตฌ์ฑํ๋ Unit Identifier๋ ๋ชจ๋๋ฒ์ค TCP ์์๋ IP ์ฃผ์๋ก ์๋ณํ๊ธฐ ๋๋ฌธ์ Unit Identifier๋ ์ฌ์ฉ๋์ง ์๋ ํญ๋ชฉ์ด์ง๋ง 255(0xFF)๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํ๋ค. โป LS ์ฐ์ ์ ์๋ดํ์ง ํฅ์ ๊ต์ก ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด๋ด๋ 0xFF๋ก ๊ณ ์ ํ๋ค๊ณ ํ๋ค.
Function Code
์ผ๋ฐ์ ์ธ ์ธก์ ๊ฐ์ ํํํ๋ ์๋ ๋ก๊ทธ์ 0๊ณผ 1๋ก ๊ตฌ์ฑ๋๋ ์ํ๊ฐ์ ํํํ๋ ๋์งํธ๋ก ๋๋์ด์ ๊ธฐ๋ฅ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ค. ์ฃผ์ ๋ฒ์์ ๋ฐ๋ผ 01(0x01, Read Coil)๊ณผ 02(0x02, Read Discrete Inputs)๋ฅผ ์ฌ์ฉํ๋ฉฐ ์๋ ๋ก๊ทธ๋ ์ฃผ๋ก 03(0x03, Read Holding Registers)๋ฅผ ์ฌ์ฉํ๋ ํธ์ด๋ค. โป ๊ธฐ๋ฅ ์ฝ๋ํ๋ฅผ ์ดํด๋ณด๋ฉด 02(0x02, REad Discrete Inputs)์ 04(0x04, Read Input Registers)๋ ์ฝ๊ธฐ๋ง ์ํํ๋ ๋ฉ๋ชจ๋ฆฌ ๋ฒ์์์ ์ฌ์ฉํ๊ฒ ๋๋ค๋ ๊ฒ์ ์์์ฑ ์ ์๋ค.
Example
๋คํฐ ๊ธฐ๋ฐ์ digitalpetri/modbus ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ผ๋ steveohara/j2mod๋ผ๋ ์๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ๋ชจ๋๋ฒ์ค TCP ํต์ ์ ๊ตฌํํ๋๊ฒ ๊ฐ๋จํ ๊ฒ ๊ฐ๋ค. ๋ค์์ ์ฝ๋๋ ๊ฐ๋จํ๊ฒ ์ฌ๋ ์ด๋ธ์ ๋ชจ๋๋ฒ์ค ๋งต์ ์ด๋ฏธ์ง๋ก ์ ์ํ๊ณ ๋ง์คํฐ์์ ์ ์๋ ๋ชจ๋๋ฒ์ค ๋งต์ ๋ํด์ ์ฝ์ด๋ณด๋ ์ํ์ด๋ค.
implementation 'com.ghgande:j2mod:3.1.1'
@Slf4j
public class ModbusTCP {
public static final int TCP_UNIT_ID = 255; // 0xFF
public static void main(String[] args) throws Exception {
DefaultProcessImageFactory imageFactory = new DefaultProcessImageFactory();
ProcessImageImplementation memoryMap = imageFactory.createProcessImageImplementation();
memoryMap.addDigitalOut(imageFactory.createDigitalOut(true));
ObservableRegister register = new ObservableRegister();
register.setValue(1024);
memoryMap.addRegister(register);
ModbusSlave tcpSlave = ModbusSlaveFactory.createTCPSlave(Modbus.DEFAULT_PORT, 5, false);
tcpSlave.addProcessImage(TCP_UNIT_ID, memoryMap);
tcpSlave.open();
ModbusTCPMaster modbusTCPMaster = new ModbusTCPMaster("127.0.0.1", Modbus.DEFAULT_PORT);
modbusTCPMaster.connect();
if (modbusTCPMaster.isConnected()) {
BitVector bitVector = modbusTCPMaster.readCoils(TCP_UNIT_ID, 0, 1);
boolean[] bools = new boolean[bitVector.size()];
for (int i = 0; i < bitVector.size(); i++) {
bools[i] = bitVector.getBit(i);
}
Register[] registers = modbusTCPMaster.readMultipleRegisters(TCP_UNIT_ID, 0, 1);
log.info("booleans, {}", bools);
log.info("registers, {}", registers);
modbusTCPMaster.disconnect();
}
ModbusSlaveFactory.close();
}
}
์ฌ๋ ์ด๋ธ ์ด๋ฏธ์ง ๊ตฌ์ฑ ์ 10001-20000์ DigitalIn๋ฅผ ์ฌ์ฉํ๋ฉฐ 30001-40000์ InputRegister๋ก ๋ง๋ค ์ ์๋ค. ์ฝ๊ธฐ ๋ฟ๋ง ์๋๋ผ ์ฐ๊ธฐ๋ ๊ฐ๋ฅํ DigitalOut๊ณผ Register์ ๋ํด์๋ ์ฌ๋ ์ด๋ธ์์ ๋ณ๊ฒฝ์ ๋ํด ๊ฐ์งํ ์ ์๋ ObservableDigitalOut๊ณผ ObservableRegister๋ฅผ ์ ๊ณตํ๋ค.
PLC Simulator
ModRSsim2 ํ๋ก๊ทธ๋จ์ ํตํด TCP/IP ๊ธฐ๋ฐ์ PLC ์ฌ๋ ์ด๋ธ์ ๋ํ ์๋ฎฌ๋ ์ดํฐ๋ฅผ ์คํํ ์ ์๋ค. ์ ์ฝ๋์์ ์ฌ๋ ์ด๋ธ ๋ฉ๋ชจ๋ฆฌ ๋งต ์ ๋ณด๋ฅผ ๋ง๋ค๊ธฐ๋ณด๋ค ์๋ฎฌ๋ ์ดํฐ ํ๋ก๊ทธ๋จ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฒ ์ ์ํ๊ณ ๋ชจ๋๋ฒ์ค ๋ง์คํฐ์ธ ํด๋ผ์ด์ธํธ ๋ก์ง ๊ตฌํ์ ์ง์คํ ์ ์์ ๊ฒ์ด๋ค.