본문으로 건너뛰기

OPC-UA 드라이버

개요​

OPC Unified Architecture (IEC 62541) 기반 산업 데이터 표준 프로토콜.

항목값
opc_typeOPCUA
구현 클래스plantpulse.driver.protocol.opcua.OPCUA_34_Driver
라이브러리Eclipse Milo (milo-sdk-client)
read✅
write✅ (node.writeValue)
보안사용자 인증 Anonymous, Username/Password. 채널 보안 None · Sign · SignAndEncrypt · auto

Kepware, Matrikon, Prosys 등 대부분의 OPC-UA 서버와 호환됩니다.


OPC 등록 폼​

필드의미예시
opc_agent_ipOPC-UA 서버 IP192.168.0.40
opc_agent_portOPC-UA 서버 TCP 포트49320 (Kepware 기본)
timecycle폴링 주기 (ms)1000
options.servernameendpoint 끝의 서버명(Kepware 면 비움)
options.username인증 사용자kopens
options.password인증 패스워드***
options.discoverytrue 면 connect 때 RootFolder 부터 노드 트리 전체를 브라우징해 로그에 남김 (엔드포인트 검색 아님 — 엔드포인트는 화면의 [엔드포인트 조회])false (권장)
options.security_mode채널 보안 모드. None(기본) · Sign · SignAndEncrypt · auto(Sign·SignAndEncrypt 중 가장 강한 것). None 이 아니면 None 으로 내려가지 않음. 대소문자 무시, 모르는 값은 OPCUA_SECURITY_CONFIG_INVALIDSignAndEncrypt
options.security_policyauto(기본) · Basic256Sha256 · Aes128_Sha256_RsaOaep · Aes256_Sha256_RsaPss. auto 는 Aes256_Sha256_RsaPss > Basic256Sha256 > Aes128_Sha256_RsaOaep 순. 보안 모드 None 이면 무시auto
options.allow_deprecated_policytrue 면 폐기된 Basic256 · Basic128Rsa15 도 허용(auto 후보에 들어가고 명시도 가능). true/false 외 값은 OPCUA_SECURITY_CONFIG_INVALIDfalse

생성되는 endpoint URL 형식:

opc.tcp://<opc_agent_ip>:<opc_agent_port>/<servername>

username + password 가 모두 있으면 UsernameProvider, 없으면 AnonymousProvider 사용.

pki_dir(클라이언트 인증서와 신뢰 목록 디렉터리)는 입력 칸이 아닙니다. edge-web 이 보안 모드가 None 이 아닐 때만 $PE_DATA_DIR/pp-data/opcua-client 를 넣고, 연결 행에 저장된 값은 버립니다.

엔드포인트 선택:

  • security_mode=None (OPCUA_34_Driver.selectEndpoint) — 서버가 광고한 목록에서 MessageSecurityMode.None 인 것만 고릅니다. 쓸 토큰 종류(UserName / Anonymous)를 광고한 엔드포인트가 우선이고, 없으면 첫 None 엔드포인트입니다.
  • 그 밖의 모드 (OpcuaEndpointSelector.selectSecure) — 받아들이는 모드·정책과 쓸 토큰 종류를 모두 만족하는 엔드포인트 가운데 가장 강한 것을 고릅니다. 없으면 None 으로 내려가지 않고 OPCUA_NO_MATCHING_ENDPOINT 로 실패합니다.

고른 엔드포인트 URL 의 호스트는 opc_agent_ip 로 바꿉니다(포트·경로는 서버가 광고한 값). 원인을 아는 실패(인증서 · 보안 설정 · 맞는 엔드포인트 없음)는 OPCUA connect refused: error_code=<코드> opc_id=<OPC ID> ... WARN 한 줄이 남습니다(같은 원인이면 한 번만). OPCUA_CONNECT_FAILED · OPCUA_PKI_ERROR 는 매번 OPCUA connect failed: ... ERROR 와 스택이 남습니다.

보안 연결의 화면 절차

서버 인증서 신뢰, 엣지 인증서 내려받기, 오류 문구는 OPC-UA 연결 — 보안 연결 에 있습니다. 처음 보는 서버 인증서는 거절되고(첫 접속 자동 신뢰 없음), 운영자가 화면에서 신뢰한 뒤에 연결됩니다.


태그 plc_address 형식​

OPC-UA 표준 NodeId 표기. 예:

표기의미
ns=2;s=Simulator.Sine1namespace 2, string identifier Simulator.Sine1
ns=2;i=1234numeric identifier 1234
ns=3;g=09087e75-8e5e-499b-954f-f2a8624db28aGUID identifier
ns=2;b=Mb=opaque (bytestring) identifier

Kepware 의 경우 보통 ns=2;s=<Channel>.<Device>.<Tag> 형식.


data_type / format 매핑​

OPC-UA 변수 노드의 Value 를 String 으로 캐스팅 후 반환합니다. data_type 은 후속 단계 (PLCValueFomula, Sparkplug 발행 등) 가 캐스팅에 사용합니다.

data_type동작
Float / Double숫자 그대로
Int / Integer / Int32 / Short / Long숫자 그대로
Boolean / Booltrue/false 문자열
String / TextUTF-8 문자열
DateTimeISO 문자열

format 은 OPC-UA 에서는 일반적으로 사용하지 않습니다. 후처리 수식이 필요하면 fomula 사용.


흔한 에러 + 해결​

메시지 / 증상원인해결
connection refused서버 미가동, 포트 차단telnet <ip> <port> 로 통신 확인
Bad_UserAccessDeniedusername/password 오류, 익명 비허용서버측 사용자 등록 또는 anonymous 활성화
OPCUA_CLIENT_CERT_REJECTED (Bad_SecurityChecksFailed 등)서버가 엣지 클라이언트 인증서를 신뢰하지 않음엣지 인증서(.der)를 서버 신뢰 목록에 넣음
OPCUA_NO_MATCHING_ENDPOINT서버가 그 모드·정책·토큰의 엔드포인트를 광고하지 않음화면의 [엔드포인트 조회] 로 서버가 주는 조합을 확인해 맞춤
포트가 맞지 않아 연결 실패드라이버는 광고된 엔드포인트의 호스트만 opc_agent_ip 로 바꾸고 포트·경로는 그대로 씀NAT·포트 포워딩으로 포트를 바꿔 노출했다면 서버가 광고하는 포트와 같게 맞춤. hosts 매핑은 필요 없음
값이 항상 0 / nullNodeId 오타, namespace 번호 불일치UA Expert 등 클라이언트로 같은 NodeId read 검증

curl 등록 예시​

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_UA_Kepware",
"opc_type": "OPCUA",
"opc_name": "Kepware",
"opc_agent_ip": "192.168.0.40",
"opc_agent_port": "49320",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": {
"username": "kopens",
"password": "***",
"discovery": "false"
},
"tag_list": [
{
"tag_id": "OPC_UA_Kepware_TAG_00001",
"tag_name": "Sine1",
"plc_address": "ns=2;s=Simulator.Sine1",
"data_type": "Float",
"description": "Sine wave"
},
{
"tag_id": "OPC_UA_Kepware_TAG_00002",
"tag_name": "Random1",
"plc_address": "ns=2;s=Simulator.Random1",
"data_type": "Integer"
}
]
}'

값 read:

curl -s http://<edge-host>/api/v1/tag/OPC_UA_Kepware_TAG_00001/value | jq

참고: Kepware 빠른 설정 가이드​

  1. Kepware Server → Project → 채널 추가 (예: Simulator).
  2. 디바이스 추가 (예: Simulator) → 모델 = 16 Bit Device.
  3. 태그 추가 → Sine1, type Float, scan rate 100ms 등.
  4. Project Properties → OPC UA → Server Endpoint 에서 포트 49320 활성화.
  5. Anonymous 또는 사용자 계정 활성화. 보안 연결이면 엔드포인트 보안 정책에 Basic256Sha256 이상과 Sign and Encrypt 를 켜고, 엣지 인증서를 신뢰 목록에 넣음.

→ Edge 등록 폼:

필드값
opc_agent_ipKepware 서버 IP
opc_agent_port49320
options.servername(비움)
options.username/password위에서 만든 계정
plc_addressns=2;s=Simulator.Simulator.Sine1

예제 모음 (데이터타입별)​

OPC-UA 는 변수 노드의 값(Variant)을 String 으로 캐스팅 후 반환합니다. data_type 은 후처리 (PLCValueFomula, Sparkplug DataTypeMapper) 가 캐스팅에 사용. format 은 일반적으로 사용하지 않습니다.

Boolean​

data_typeplc_address 예비고
Booleanns=2;s=Simulator.Boolean1Kepware Simulator 의 Boolean tag
Booleanns=2;s=PLC.Status.RunningKepware Channel.Device.Tag
Booleanns=2;i=1003numeric NodeId

16/32-bit 정수​

data_typeplc_address 예비고
Integerns=2;s=Simulator.Random1Random integer 시뮬
Integerns=2;s=Simulator.Counter1Counter
Integerns=2;s=PLC.D100Kepware MELSEC 채널 노출

64-bit 정수​

data_typeplc_address 예비고
Longns=2;s=Simulator.LongCounterUInt64 / Int64 노드
Longns=2;s=PLC.Cycle.Total누적 카운터

실수​

data_typeplc_address 예비고
Floatns=2;s=Simulator.Sine1sine wave 시뮬
Floatns=2;s=Simulator.Ramp1ramp wave
Doublens=2;s=PLC.Pressure정밀 측정값

문자열​

data_typeplc_address 예비고
Stringns=2;s=Simulator.User1.Name문자열 노드
Stringns=2;s=PLC.Recipe.Active레시피명

NodeId 형식별​

identifier 타입plc_address 예
String (s=)ns=2;s=Simulator.Sine1
Numeric (i=)ns=2;i=1234
GUID (g=)ns=3;g=09087e75-8e5e-499b-954f-f2a8624db28a
Opaque (b=)ns=2;b=Mb=

Formula 활용​

용도data_typefomula비고
단위 변환Float${VALUE}*0.001g → kg 등
영점 보정Float${VALUE}-${TAG_ZERO}다른 OPCUA 태그 참조 가능
형변환Float${VALUE}*1.0Integer → Float 강제
다항식Float${VALUE}*${VALUE}*0.0001비선형

curl 종합 예제 (모든 타입)​

curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_UA_FULL",
"opc_type": "OPCUA",
"opc_name": "Kepware Full",
"opc_agent_ip": "192.168.0.40",
"opc_agent_port": "49320",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": { "discovery": "false" },
"tag_list": [
{"tag_id":"OPC_UA_FULL_T01", "tag_name":"BoolTag", "plc_address":"ns=2;s=Simulator.Boolean1", "data_type":"Boolean"},
{"tag_id":"OPC_UA_FULL_T02", "tag_name":"IntTag", "plc_address":"ns=2;s=Simulator.Random1", "data_type":"Integer"},
{"tag_id":"OPC_UA_FULL_T03", "tag_name":"LongTag", "plc_address":"ns=2;s=Simulator.LongCounter", "data_type":"Long"},
{"tag_id":"OPC_UA_FULL_T04", "tag_name":"FloatTag", "plc_address":"ns=2;s=Simulator.Sine1", "data_type":"Float"},
{"tag_id":"OPC_UA_FULL_T05", "tag_name":"DoubleTag", "plc_address":"ns=2;s=PLC.Pressure", "data_type":"Double"},
{"tag_id":"OPC_UA_FULL_T06", "tag_name":"StringTag", "plc_address":"ns=2;s=Simulator.User1.Name", "data_type":"String"},
{"tag_id":"OPC_UA_FULL_T07", "tag_name":"NumIdTag", "plc_address":"ns=2;i=1234", "data_type":"Integer"},
{"tag_id":"OPC_UA_FULL_T08", "tag_name":"ScaledTag", "plc_address":"ns=2;s=Simulator.Sine1", "data_type":"Float", "fomula":"${VALUE}*100"}
]
}'