본문으로 건너뛰기

CANopen 드라이버

개요

CANopen — CAN 버스 위의 EN 50325-4 / CiA 301 표준 application layer (모터드라이브 / 인코더 / IO 모듈에서 보편). 2026-07 네이티브 재작성 — 옛 Apache PLC4j canopen 위임은 제거되었습니다(PLC4X CANopen 은 로컬 SocketCAN transport 만 지원해 TCP 로 나를 수 없어, 물리 CAN 버스가 없는 엣지에서 connect 가 죽었음). 지금은 plantpulse-plc-protocol 의 자체 SDO expedited upload 구현(CanopenSdoClient) + transport 추상화(CanTransport)를 사용 — AB-ETH / BACnet 와 동일한 네이티브 패턴.

항목
opc_typeCANOPEN
구현 클래스plantpulse.driver.protocol.canopen.CANOpenDriver
라이브러리자체 네이티브 (plantpulse-plc-protocolcanopen — SDO expedited upload)
상속BaseProtocolDriver (PLC4j 아님)
getProtocol()"canopen:tcp" (옛 PLC4X scheme 표기 유지 — 로그/진단 호환)
기본 포트20200 (CAN-over-TCP 게이트웨이)
상태안정 (SDO expedited 폴링)
readOK (SDO expedited upload, 값 ≤4바이트)
write❌ (읽기 전용 — SDO download 미구현)
보안없음
CAN-over-TCP 게이트웨이 전제

기본 transport(tcp)는 CAN ↔ TCP 게이트웨이 너머의 CANopen 노드와 통신합니다. CAN 프레임을 canId(4B BE) + dlc(1B) + data[dlc] 자체 바이너리 프레이밍으로 TCP 에 실어 나릅니다(핸드셰이크/버스명 협상 없음 — 시뮬레이터와 동일 프레이밍). transport=socketcan(로컬 can0)은 현재 스텁(AF_CAN 네이티브 바인딩/JNI 필요)이라 connect 가 실패합니다 — 물리 CAN 인터페이스 트랙은 후속.


클래스 구조

BaseProtocolDriver
└── CANOpenDriver — connect() 에서 CanTransport(tcp/socketcan) + CanopenSdoClient 생성

read 실패(Abort/타임아웃/소켓)는 소켓을 무효화(connected=false + close)해 엣지가 재연결하도록 유도합니다.


wire 포맷 요약 (자체 구현 — SDO expedited upload)

  • 요청: COB-ID 0x600+nodeId, data [0x40, index LE(2B), subindex, 0×4].
  • 응답: COB-ID 0x580+nodeId, command 0x43(4B)/0x47(3B)/0x4B(2B)/0x4F(1B) — 값 little-endian. 0x80 = Abort(코드 u32 LE) → 통신 예외.
  • segmented / block transfer 미지원(값 >4B, 예: 긴 문자열) — 스칼라 태그 폴링엔 expedited 로 충분.

OPC 등록 옵션

필드의미기본
opc_agent_ipCAN-TCP 게이트웨이 IP192.168.1.20
opc_agent_port게이트웨이 TCP 포트20200
options.read-timeoutSDO 응답 타임아웃 (ms)3000
options.transporttcp / socketcan(스텁)tcp
options.can-interfacesocketcan 모드 인터페이스명can0
옛 PLC4X 옵션 제거

옛 폼/문서의 node-id 옵션은 폐기 — 노드 ID 는 이제 태그 주소의 1번째 필드입니다(아래). request-timeout/heartbeat 옵션도 네이티브가 받지 않습니다(타임아웃은 read-timeout).


태그 plc_address 형식 (신규 계약)

<nodeId>:<index>:<subindex>[:<TYPE>]
필드의미표기
nodeIdCANopen 노드 id (1..127)10진
index오브젝트 딕셔너리 index (0..0xFFFF)0x 접두 16진(0x6041) 또는 10진
subindex서브 index (0..255)0x 접두 16진 또는 10진
TYPE (선택)LE 바이트 해석 타입생략 시 data_type 유추 → 그것도 없으면 UINT16
표기의미
1:0x6041:0:UINT16노드 1, DS402 Statusword
3:0x2000:0:REAL노드 3, 제조사 영역 float32
1:0x6064:0:INT32Position actual value (32-bit signed)
5:8192:1노드 5, index 10진 8192(=0x2000), TYPE 생략 → UINT16

TYPE 토큰 (별칭 포용)

토큰(별칭)크기해석
BOOL(BOOLEAN)10 아니면 true
UINT8(U8,USINT,BYTE) / INT8(I8,SINT)1u8 / i8
UINT16(U16,UINT,WORD) / INT16(I16,INT)2u16 / i16 (기본 UINT16)
UINT32(U32,UDINT,DWORD) / INT32(I32,DINT)4u32 / i32
REAL(FLOAT,F32,REAL32)4IEEE-754 float (LE)
STRING(STR,VISIBLE_STRING)≤4ASCII (trailing NUL 제거)

TYPE 생략 시 data_type 유추: Boolean→BOOL, Integer→INT16, Long→INT32, Float→REAL, Double→REAL(4B 근사), String→STRING.


support / 미지원

  • read: SDO expedited upload (≤4바이트) — OK.
  • write: 미지원isWriteSupported() = false, write() 는 항상 false. (SDO download 는 후속.)
  • segmented / block SDO, PDO 매핑, NMT / Heartbeat 모니터링, SYNC, EMCY, LSS: 미지원.
  • 직접 SocketCAN / USB-CAN: 스텁 (CAN-over-TCP 게이트웨이 전제).

curl 등록 예시

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_CANOPEN_DRIVE1",
"opc_type": "CANOPEN",
"opc_name": "Servo Drive 1",
"opc_agent_ip": "192.168.1.20",
"opc_agent_port": "20200",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": { "read-timeout": "3000" },
"tag_list": [
{"tag_id":"OPC_CANOPEN_DRIVE1_T01","tag_name":"Statusword","plc_address":"1:0x6041:0:UINT16","data_type":"Integer"},
{"tag_id":"OPC_CANOPEN_DRIVE1_T02","tag_name":"ActualPos","plc_address":"1:0x6064:0:INT32","data_type":"Long"}
]
}'

참고

  • CiA 301 (Application Layer) / CiA 402 (Drives) 표준.
  • 코드: plantpulse-edge-driver/src/plantpulse/driver/protocol/canopen/CANOpenDriver.java + plantpulse-plc-protocolcanopen 패키지.
  • 운영: 게이트웨이 프레이밍은 자체 바이너리(canId+dlc+data) — socketcand 텍스트 프로토콜과 다름. 좌표는 device EDS 파일 기준으로 검증 권장.