PythonでOPC UAの変数ノードを監視する
opcua-asyncioを使ってOPC UAの変数ノードの変更を購読し、ポーリングではなくサーバーからのプッシュ更新にクライアントが反応するようにします。
必要要件
以下のコマンドでopcua-asyncioをインストールします。
pip install asyncuaPythonスクリプト
import asyncioimport random
from asyncua import Server
ENDPOINT = 'opc.tcp://localhost:4840'NAMESPACE = 'http://examples.freeopcua.github.io'
async def main() -> None: # Start a server. server = Server() await server.init() server.set_endpoint(ENDPOINT) idx = await server.register_namespace(NAMESPACE) await server.start() print(f'Server started: {server}')
# Create a node. myobj = await server.get_objects_node().add_object(idx, 'MyObject') myvar = await myobj.add_variable(idx, 'MyVariable', 1) await myvar.set_writable()
# Write a new value every second. while True: await myvar.write_value(random.randint(1, 100)) await asyncio.sleep(1)
if __name__ == '__main__': asyncio.run(main())import asyncio
from asyncua import Client, Nodefrom asyncua.common.subscription import DataChangeNotif, SubHandler
ENDPOINT = 'opc.tcp://localhost:4840'NAMESPACE = 'http://examples.freeopcua.github.io'
class MyHandler(SubHandler): def __init__(self): self._queue = asyncio.Queue()
def datachange_notification(self, node: Node, value, data: DataChangeNotif) -> None: self._queue.put_nowait([node, value, data]) print(f'Data change notification was received and queued.')
async def process(self) -> None: try: while True: # Get data in a queue. [node, value, data] = self._queue.get_nowait() path = await node.get_path(as_string=True)
# *** Write your processing code ***
print(f'New value {value} of "{path}" was processed.')
except asyncio.QueueEmpty: pass
async def main() -> None: async with Client(url=ENDPOINT) as client: # Get a variable node. idx = await client.get_namespace_index(NAMESPACE) node = await client.get_objects_node().get_child([f'{idx}:MyObject', f'{idx}:MyVariable'])
# Subscribe data change. handler = MyHandler() subscription = await client.create_subscription(period=0, handler=handler) await subscription.subscribe_data_change(node)
# Process data change every 100ms while True: await handler.process() await asyncio.sleep(0.1)
if __name__ == '__main__': asyncio.run(main())テスト
以下のコマンドでサーバーを起動します。
$ python server.pyServer started: OPC UA Server(opc.tcp://localhost:4840)以下のコマンドでクライアントを起動します。
$ python client.pyData change notification was received and queued.New value 4 of "['0:Root', '0:Objects', '2:MyObject', '2:MyVariable']" was processed.Data change notification was received and queued.New value 79 of "['0:Root', '0:Objects', '2:MyObject', '2:MyVariable']" was processed.Data change notification was received and queued.New value 75 of "['0:Root', '0:Objects', '2:MyObject', '2:MyVariable']" was processed....まとめ
opcua-asyncioを使ってOPC UAの変数ノードを購読することで、値の変更をポーリングするのではなく、サーバーからプッシュされた変更にクライアントが反応できるようになりました。このプッシュ型パターンを支えているのはcreate_subscriptionとsubscribe_data_changeです。クライアント側が値をポーリングして比較する必要はなく、MyVariableが変化するたびにサーバー側が自身のスケジュールでdatachange_notificationを呼び出します。そのコールバックをdatachange_notification内で直接処理するのではなくasyncio.Queueを経由させることで、同期的な通知ハンドラを高速に保ちつつ、100msごとにキューをポーリングする非同期のprocess()ループから切り離しています。この分離によって、このパターンは再利用しやすくなっています。各監視タスクに必要なロジックは、通知の受け取り方に手を加えることなく、「write your processing code」のブロック内に記述するだけで済みます。
Related posts
TesseractとPytesseractによる日本語PDFのOCR処理
Tesseract OCR v4とpytesseractを使ってPDFから日本語テキストを抽出し、出力をきれいにするために必要な正規化処理についても扱います。
インターネット接続なしで Python パッケージをインストールする
インターネットに接続できない環境向けに、別のマシンで pip download したパッケージを転送し、--find-links と --no-index でオフラインインストールする方法。
EC2上でProxy.pyを軽量HTTPプロキシとして動かす
Proxy.pyにはそれ自体の認証機構がないため、EC2インスタンス上で動かしつつSSHトンネル経由で安全にアクセスする方法です。
SiteWise Edge GatewayでOPC UAデータをKinesisにストリーミングする
SiteWise Edge Gatewayとカスタムのgreengrassコンポーネントを使って、OPC UAテレメトリをKinesis Data Streamsへ橋渡しします。

保守性とテストしやすさのための依存性注入
密結合なTypeScriptクラスを、モックでテストできるクラスへとリファクタリングする。給与計算機、システムクロック、SESを対象に依存性注入を適用する。
