시뮬레이터로 시험 흐름 만들기
PDComm Modbus 시뮬레이터는 세 가지 형태로 제공됩니다. GUI, 헤드리스, MCP 서버는 같은 명령과 같은 프로젝트 형식을 씁니다. 이 튜토리얼은 GUI에서 만든 상태를 파일로 옮겨 헤드리스와 MCP에서 다시 쓰는 흐름을 따라 합니다.
각 프로세스는 독립된 세션입니다. MCP 서버나 헤드리스 시뮬레이터를 시작해도 이미 켜 둔 GUI의 상태를 보지 못합니다. 세션 사이에서 상태를 옮기려면 실제 TCP 통신이나 프로젝트 파일을 씁니다.
- GUI 시뮬레이터를 TCP 서버로 열고 콘솔 클라이언트로 값을 씁니다.
- GUI의 Data, Trace / Fault 탭에서 결과를 확인하고 프로젝트로 저장합니다.
- 프로젝트를 헤드리스 시뮬레이터에서 불러오고, 시나리오 파일로 회귀 시험을 만듭니다.
- MCP 서버로 같은 명령을 호출합니다.
준비물
섹션 제목: “준비물”- PDComm Modbus SDK, 빠른 시작에서 빌드한 C++ 콘솔
- 데스크톱 환경. 화면이 없는 Linux 서버에서는 GUI 단계를 건너뛰고 헤드리스로 대신합니다.
jq(JSON 명령줄 도구)
1. GUI를 TCP 서버로 엽니다
섹션 제목: “1. GUI를 TCP 서버로 엽니다”시뮬레이터를 시작합니다.
- macOS:
open "$MODBUS_SDK/PDComm Modbus Simulator.app" - Linux:
"$MODBUS_SDK/bin/pdcomm-modbus-simulator" - Windows: SDK의
bin\pdcomm-modbus-simulator.exe
Connection 탭에서 다음과 같이 지정하고 Connect를 누릅니다.
| 항목 | 값 |
|---|---|
| Transport | TCP |
| Role | Server |
| TCP address | 127.0.0.1:1502 |
| Unit | 1 |
Connect는 현재 입력값으로 configure를 보낸 뒤 connect를 보냅니다. 성공하면 버튼이 Disconnect로 바뀌고, 아래 상태 줄에 Completed: connect가 표시됩니다. 포트를 0으로 지정했다면 TCP address가 운영체제가 고른 실제 주소로 바뀝니다.


연결에 실패하면 상태 줄에 connect failed:와 오류 메시지가 나옵니다. 예를 들어 같은 포트를 다른 프로그램이 쓰고 있으면 connect failed: Io(AddrInUse, ...)입니다.
2. 콘솔 클라이언트로 값을 씁니다
섹션 제목: “2. 콘솔 클라이언트로 값을 씁니다”다른 터미널에서 C++ 콘솔을 시작하고 빠른 시작의 4단계와 같이 연결해 두 레지스터를 씁니다.
{"action":"configure","config":{"transport":"tcp","role":"client","address":"127.0.0.1:1502","port":"","unit":1,"baud":19200,"parity":"even","stop_bits":1,"timeout_ms":1000}}{"action":"connect"}{"action":"request","pdu":"1000000002040040002A"}{"action":"request","pdu":"0300000002"}{"ok":true,"result":{"response":[16,0,0,0,2],"source":"wire","status":"success"}}{"ok":true,"result":{"response":[3,4,0,64,0,42],"source":"wire","status":"success"}}1000000002040040002A는 보유 레지스터 0~1에 64와 42를 쓰는 기능 16 요청입니다.
3. GUI에서 결과를 확인합니다
섹션 제목: “3. GUI에서 결과를 확인합니다”- Data 탭에서 Read model을 누르면 모델 전체가 JSON으로 나옵니다.
holding_registers의values가[64, 42, 0, ...]로 시작합니다. - Trace / Fault 탭에서 Read trace를 누르면 서버가 받은 요청과 응답이 16진수로 보입니다.
source가wire인 항목이 실제 통신입니다.


GUI의 Request 탭에서 Execute locally를 누르면 같은 PDU를 서버 자신의 모델에서 실행합니다. Send to peer는 클라이언트가 실제 상대에게 보내는 버튼입니다. 서버 역할에서 누르면 상태 줄에 request failed: Connect a client before sending a request가 나옵니다.
4. 프로젝트로 저장합니다
섹션 제목: “4. 프로젝트로 저장합니다”Project 메뉴의 **Save…**를 골라 plant.modbus.json으로 저장합니다. 프로젝트 파일에는 Connection 탭의 구성과 모델 전체가 들어갑니다.
{ "config": {"address": "127.0.0.1:1502", "role": "server", "transport": "tcp", ...}, "kind": "pdcomm.modbus.project", "model": {"holding_registers": {"start": 0, "values": [64, 42, 0, ...]}, ...}, "version": 1}- Security 탭의 값도 저장됩니다. 인증서와 키는 경로만 들어가므로 다른 PC에서 열면 경로를 그 PC에 맞게 고칩니다.
- Project → **Open…**으로 불러올 수 있습니다. 연결 중이면 먼저 Disconnect합니다. 연결 중에 불러오면 상태 줄에
project_import failed: Disconnect before loading a project가 나옵니다. - 형식이 틀린 파일은 거부하고 현재 상태를 그대로 둡니다. 예를 들어
version이 1이 아니면Unsupported Modbus project kind or version입니다.
이제 GUI에서 Disconnect를 누르고 창을 닫습니다. 콘솔에는 quit를 입력합니다.
5. 헤드리스에서 프로젝트를 재생합니다
섹션 제목: “5. 헤드리스에서 프로젝트를 재생합니다”프로젝트 파일을 project_import 명령으로 감싸고, 읽기 명령을 붙여 헤드리스 시뮬레이터에 넣습니다.
jq -c '{action:"project_import",project:.}' plant.modbus.json > replay.jsonlecho '{"action":"execute","pdu":"0300000002"}' >> replay.jsonl"$MODBUS_SDK/bin/pdcomm-modbus-headless" < replay.jsonl{"ok":true,"result":{"loaded":true}}{"ok":true,"result":{"response":[3,4,0,64,0,42],"source":"local","status":"success"}}헤드리스 시뮬레이터는 입력이 끝나면(EOF) 종료합니다. 모든 명령이 성공했으므로 종료 코드는 0입니다. 같은 프로젝트를 불러와 connect하면 GUI 대신 헤드리스가 같은 서버 역할을 합니다.
6. 시나리오로 회귀 시험을 만듭니다
섹션 제목: “6. 시나리오로 회귀 시험을 만듭니다”시나리오(scenario)는 명령과 기대 결과의 배열입니다. expect에 적은 필드만 결과와 정확히 비교합니다. 다음 내용으로 regression.json을 만듭니다.
[ {"command":{"action":"data_set","table":"holding_registers","address":0,"values":[42]}}, {"command":{"action":"execute","pdu":"0300000001"},"expect":{"response":[3,2,0,42],"status":"success"}}, {"command":{"action":"execute","pdu":"0300000000"},"expect":{"response":[131,3],"status":"exception"}}]"$MODBUS_SDK/bin/pdcomm-modbus-headless" --scenario regression.json{"result":{"written":1},"step":1}{"result":{"response":[3,2,0,42],"source":"local","status":"success"},"step":2}{"result":{"response":[131,3],"source":"local","status":"exception"},"step":3}종료 코드는 0입니다. 두 번째 단계의 기대값을 [3,2,0,43]으로 바꿔 다시 실행하면 첫 단계만 출력한 뒤 표준 오류에 Scenario step 2 field response did not match를 쓰고 종료 코드 1로 끝납니다. CI에서 종료 코드로 성공과 실패를 판정할 수 있습니다.
7. MCP 서버로 같은 명령을 호출합니다
섹션 제목: “7. MCP 서버로 같은 명령을 호출합니다”MCP 서버는 같은 명령을 modbus_<action> 도구로 제공합니다. 보통은 AI 도구가 실행하지만, 표준 입력으로 JSON-RPC를 넣어 직접 확인할 수 있습니다.
printf '%s\n' \ '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"tutorial","version":"1"}}}' \ '{"jsonrpc":"2.0","method":"notifications/initialized"}' \ '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"modbus_execute","arguments":{"pdu":"0300000001"}}}' \ | "$MODBUS_SDK/bin/pdcomm-modbus-mcp"{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"tools":{"listChanged":false}},"protocolVersion":"2025-11-25","serverInfo":{"name":"pdcomm-modbus","version":"1.0.0"}}}{"id":2,"jsonrpc":"2.0","result":{"content":[{"text":"{\"response\":[3,2,0,0],\"source\":\"local\",\"status\":\"success\"}","type":"text"}],"isError":false}}새 MCP 세션의 모델은 비어 있으므로 값이 0입니다. 앞의 GUI나 헤드리스 상태를 보려면 modbus_project_import로 같은 프로젝트를 불러옵니다. AI 도구 연결 방법은 가이드: MCP 서버에 있습니다.
다음 단계
섹션 제목: “다음 단계”- GUI의 모든 탭은 가이드: 시뮬레이터 GUI에서 설명합니다.
- 실패를 재현하고 기록하는 방법은 실패 진단과 문의에서 이어집니다.