pdcomm-vserial 명령과 랩 파일
pdcomm-vserial은 랩(lab)을 검사하고 실행하는 명령줄 프로그램입니다. 랩의 개념은 가상 시리얼 포트의 개념에서 설명합니다.
| 명령 | 하는 일 |
|---|---|
pdcomm-vserial run --config <파일> |
랩을 시작하고 표준 입력의 제어 명령을 처리합니다. |
pdcomm-vserial check --config <파일> |
랩 파일을 검사만 합니다. 올바르면 {"valid":true}를 출력합니다. |
pdcomm-vserial pair [--instant] [--link-a <경로>] [--link-b <경로>] |
의사 터미널 두 개를 널 모뎀으로 이은 랩을 바로 시작합니다. 기본 9600bps 8N1, realistic 진행입니다. --instant는 지연 없이 전달합니다. |
pdcomm-vserial daemon --config <파일> |
서비스로 실행합니다. 표준 입력을 읽지 않고 SIGTERM이나 SIGINT를 받으면 랩을 정리하고 끝납니다. macOS와 Linux에서만 동작합니다. |
pdcomm-vserial ports list |
Windows: 설치한 포트를 보여 줍니다. |
pdcomm-vserial ports install --inf <INF> [--count N] [--first-com N] |
Windows: 포트가 N개(기본 2)가 될 때까지 만들고 드라이버를 설치합니다. 관리자 권한이 필요합니다. |
pdcomm-vserial ports remove |
Windows: 포트 장치를 모두 지웁니다. 관리자 권한이 필요합니다. |
pdcomm-vserial ports uninstall |
Windows: 포트, 드라이버 패키지, 필터 서비스를 모두 지웁니다. 관리자 권한이 필요합니다. |
pdcomm-vserial --help, --version |
사용법이나 버전을 표준 출력에 씁니다. |
종료 코드는 다음과 같습니다.
| 코드 | 의미 |
|---|---|
0 |
성공했습니다. 랩은 정상적으로 멈췄습니다. |
1 |
랩 파일 오류, 포트 열기 실패 같은 오류입니다. 표준 오류에 pdcomm-vserial: <원인>을 씁니다. |
2 |
명령이나 옵션이 잘못되었습니다. 표준 오류에 사용법을 씁니다. |
실행 중 제어
섹션 제목: “실행 중 제어”run과 pair는 랩이 준비되면 표준 출력에 포트 목록 한 줄을 씁니다.
{"ports":[{"backend":"pty","link":"/tmp/pdcomm-plc","node":"plc","path":"/dev/ttys011"},{"backend":"pty","link":"/tmp/pdcomm-hmi","node":"hmi","path":"/dev/ttys016"}],"ready":true}| 필드 | 의미 |
|---|---|
node |
노드 이름 |
backend |
pty, driver, tcp_server, rfc2217_server |
path |
응용 프로그램이 여는 장치 경로. 의사 터미널과 Linux 드라이버 포트에 있습니다. |
link |
link로 만든 심볼릭 링크 |
listen |
TCP 서버가 실제로 수신 대기하는 주소. 포트 0을 지정하면 운영체제가 고른 포트가 나옵니다. |
그 뒤 표준 입력의 각 줄을 JSON 명령 하나로 처리하고 한 줄로 응답합니다.
| 명령 | 응답 |
|---|---|
{"command":"ports"} |
{"ok":true,"ports":[…]} |
{"command":"inject","node":"b","fault":{"xor":1}} |
{"ok":true} |
{"command":"attach","node":"a","attached":false} |
{"ok":true}. false는 케이블 분리, true는 연결입니다. |
{"command":"stop"} |
응답 없이 랩을 멈추고 끝납니다. 입력 끝(EOF)도 같습니다. |
fault에는 "drop", {"xor": <0~255>}, {"error": "parity"}(parity, framing, overrun, break), {"noise": {"byte": <0~255>, "error": null}}을 씁니다. 잘못된 명령에는 {"ok":false,"error":"…"}로 응답하고 랩은 계속 실행합니다.
랩 파일
섹션 제목: “랩 파일”랩 파일은 1MiB 이하의 JSON입니다. 모르는 필드가 있으면 오류로 거부합니다. 오타가 조용히 무시되지 않습니다.
{ "engine": { "pacing": "realistic", "nodes": [ {"name": "plc", "port": {"line": {"baud": 19200, "data_bits": 8, "parity": "even", "stop_bits": "1"}}}, {"name": "hmi", "port": {"line": {"baud": 19200, "data_bits": 8, "parity": "even", "stop_bits": "1"}}} ], "media": [{"kind": "null_modem", "a": "plc", "b": "hmi"}] }, "ports": [ {"node": "plc", "backend": {"type": "pty", "link": "/tmp/pdcomm-plc"}}, {"node": "hmi", "backend": {"type": "pty", "link": "/tmp/pdcomm-hmi"}} ], "trace": "null-modem-trace.jsonl"}최상위 필드
섹션 제목: “최상위 필드”| 필드 | 설명 |
|---|---|
engine |
필수. 진행 방식, 노드, 매체를 정의합니다. |
ports |
노드를 끝점에 연결하는 바인딩 목록. 노드마다 하나까지 둡니다. |
trace |
회선 사건을 JSON Lines로 쓸 파일 경로. 상대 경로는 현재 폴더 기준입니다. |
allow_remote |
true면 루프백이 아닌 주소에서 TCP 서버를 엽니다. 기본값은 false입니다. |
bridges |
노드를 원격 RFC 2217 장치 서버에 잇는 목록 |
driver |
Linux 드라이버 제어 장치 경로. 기본값은 /dev/pdcomm-vserial입니다. |
engine
섹션 제목: “engine”| 필드 | 설명 |
|---|---|
pacing |
realistic(기본) 또는 instant |
nodes |
노드 목록 |
media |
매체 목록. kind가 null_modem, loopback, four_wire, two_wire 중 하나입니다. |
{"name": "master", "port": {"line": {"baud": 9600, "data_bits": 8, "parity": "none", "stop_bits": "2"}, "flow": "none", "rts_toggle": {"on_send": true, "delay_before_ns": 0, "delay_after_ns": 0, "rx_during_tx": false}}, "attach": {"driver_enable": {"rts": {"asserted": true}}, "echo": false}, "faults": {"seed": 7, "flip_ppm": 1000, "latency_ns": 200000, "jitter_ns": 50000}, "rx_fifo": {"trigger": 1, "idle_chars": 4}}| 필드 | 설명 |
|---|---|
name |
필수. 랩 안에서 고유한 이름 |
port.line |
baud(1data_bits(5parity, stop_bits("1", "1.5", "2"). 랩이 시작할 때의 회선 설정입니다. |
port.flow |
none, rts_cts, xon_xoff |
port.rts_toggle |
송신마다 RTS를 자동으로 올리고 내립니다. 지연(나노초)과 송신 중 수신(rx_during_tx)을 지정합니다. |
attach.driver_enable |
버스 송신 조건: "always", "auto", {"rts": {"asserted": true}} |
attach.echo |
송신 중에도 자기 문자를 받습니다. |
attach.fixed_inputs |
상대와 관계없이 켜 둘 입력 신호(cts, dsr, dcd, ri) |
faults |
확률 결함과 지연: seed, drop_ppm, flip_ppm, parity_ppm, framing_ppm, overrun_ppm, noise_ppm, latency_ns, jitter_ns |
rx_fifo |
수신 FIFO. 기본 {"trigger": 14, "idle_chars": 4}, null이면 문자마다 전달합니다. |
포트 바인딩
섹션 제목: “포트 바인딩”backend |
필드 |
|---|---|
{"type": "pty", "link": "/tmp/pdcomm-plc"} |
link는 선택입니다. 의사 터미널 경로에 고정 링크를 만들고 랩이 멈추면 지웁니다. |
{"type": "driver", "index": 0} |
설치한 드라이버의 포트 번호(0~63) |
{"type": "tcp_server", "listen": "127.0.0.1:7002"} |
원시 TCP. 회선 설정은 랩 파일의 값으로 고정됩니다. 클라이언트는 하나만 받습니다. |
{"type": "rfc2217_server", "listen": "127.0.0.1:7001"} |
RFC 2217 서버. 클라이언트가 회선 설정과 신호를 바꿉니다. |
RFC 2217로 포트 공개하기
섹션 제목: “RFC 2217로 포트 공개하기”다음 랩은 노드 device를 RFC 2217 서버로, 노드 app을 의사 터미널로 열고 두 노드를 널 모뎀으로 잇습니다.
{ "engine": { "pacing": "realistic", "nodes": [{"name": "device"}, {"name": "app"}], "media": [{"kind": "null_modem", "a": "device", "b": "app"}] }, "ports": [ {"node": "device", "backend": {"type": "rfc2217_server", "listen": "127.0.0.1:7001"}}, {"node": "app", "backend": {"type": "pty", "link": "/tmp/pdcomm-app"}} ]}pdcomm-vserial run --config rfc2217-server.jsonRFC 2217 클라이언트라면 무엇이든 127.0.0.1:7001에 연결합니다. 예를 들어 pyserial이 설치된 Python으로 다음을 실행하면 /tmp/pdcomm-app을 연 응용 프로그램이 hello를 받습니다.
python3 -c 'import serial; s = serial.serial_for_url("rfc2217://127.0.0.1:7001", baudrate=9600, timeout=1); s.write(b"hello\n")'클라이언트가 속도를 바꾸면 device 노드의 설정이 바뀝니다. 응용 프로그램 쪽 설정과 다르면 실제 회선처럼 깨진 문자가 도착합니다.
원격 장치 서버에 연결하기
섹션 제목: “원격 장치 서버에 연결하기”bridges는 로컬 노드를 다른 곳의 RFC 2217 서버에 연결합니다. 시리얼 장치 서버(device server)에 꽂은 실제 장비를 로컬 포트처럼 쓸 때 사용합니다. 브리지 노드는 포트 바인딩이 있고 매체에는 연결하지 않습니다.
{ "engine": {"nodes": [{"name": "remote", "port": {"line": {"baud": 9600}}}]}, "ports": [{"node": "remote", "backend": {"type": "pty", "link": "/tmp/pdcomm-bridge"}}], "bridges": [{"node": "remote", "rfc2217": "127.0.0.1:7001"}]}rfc2217에는 장치 서버의 호스트:포트를 씁니다. 응용 프로그램이 /tmp/pdcomm-bridge에서 바꾼 속도, 형식, 신호, 브레이크가 원격 포트로 전달됩니다.
랩 파일 오류
섹션 제목: “랩 파일 오류”check와 run은 다음 오류를 냅니다.
| 메시지 | 원인 |
|---|---|
unknown field `prot`, expected one of `name`, `port`, … |
필드 이름이 틀렸습니다. |
invalid virtual serial configuration: duplicate node a |
노드 이름이 겹칩니다. |
invalid virtual serial configuration: node a is wired twice |
한 노드를 매체 두 개에 연결했습니다. |
invalid virtual serial configuration: a two-wire bus needs at least two nodes |
2선식 버스의 노드가 부족합니다. |
invalid virtual serial configuration: baud rate must be between 1 and 16000000 |
속도가 범위를 벗어났습니다. |
invalid virtual serial configuration: data bits must be between 5 and 8 |
데이터 비트가 범위를 벗어났습니다. |
port binds unknown node x |
바인딩의 node가 노드 목록에 없습니다. |
node a is bound twice |
한 노드에 바인딩이 두 개 있습니다. |
driver port 64 is invalid or bound twice |
드라이버 포트 번호가 0~63을 벗어났거나 겹칩니다. |
0.0.0.0:7002 is not a loopback address; set allow_remote to expose it |
외부 주소로 공개하려면 allow_remote가 필요합니다. |
bridge node a needs a port binding, no wiring, and one bridge |
브리지 노드의 조건을 지키지 않았습니다. |
configuration exceeds one MiB |
파일이 1MiB를 넘습니다. |
실행 중 오류는 도구 문제 해결을 참고합니다.