Asyncua 값 읽기와 구독
한 번 읽을 때는 read_value를 사용하고, 변경될 때마다 수신하려면 구독을 생성하여 핸들러로 수신한다.
접속과 값 조회
asyncua는 asyncio 기반으로 동작하는 OPC UA 라이브러리이다. Client를 async with로 사용하면 블록에 진입할 때 접속하고 블록을 벗어날 때 접속을 종료한다.
import asyncio
from asyncua import Client
async def main():
async with Client("opc.tcp://192.0.2.20:4840") as client:
idx = await client.get_namespace_index("urn:example:line1")
node = client.get_node(f"ns={idx};s=Line1.Temperature")
print(await node.read_value())
asyncio.run(main())
get_node는 서버에 요청하지 않고 노드 객체만 생성하므로 await가 없다. 서버와 통신하는 것은 read_value이며, 값을 Python 타입으로 변환하여 반환한다. 응답 대기 시간은 Client의 timeout 인자로 지정하며 기본값은 4초이다.
변경 알림 구독
read_value를 반복 호출하면 변경되지 않은 값도 매번 요청하게 된다. 구독(subscription)을 생성하면 서버가 값을 감시하다가 알림을 전송한다.
import asyncio
class Handler:
def datachange_notification(self, node, val, data):
# 구독한 노드의 값이 바뀌면 불린다
print(node, val)
async def watch(client, node):
# 500은 알림을 보내는 주기이고 단위는 밀리초다
sub = await client.create_subscription(500, Handler())
await sub.subscribe_data_change(node)
await asyncio.sleep(60)
await sub.delete()
핸들러의 메서드 이름 datachange_notification은 고정되어 있다.
핸들러 주의 사항
핸들러 메서드는 def로도 async def로도 정의할 수 있다. 어느 경우든 클라이언트와 같은 이벤트 루프에서 호출되므로, 핸들러 내부에서 time.sleep이나 긴 계산으로 루프를 점유하면 다음 알림과 다른 요청이 모두 지연된다. 수신한 값은 asyncio.Queue에 넣고 처리는 다른 태스크에 위임한다.