On this page |
概要 ¶
PDGデータレイヤーとは? ¶
PDGデータレイヤーまたは PDGD は、PDGデータにアクセスする別の手段です。
PDGDは、定期購読ベースのシステムです。 つまり、PDGDを使用することで、PDGオブジェクトを Subscribe(定期購読) し、そのオブジェクトのデータを Watch(監視) し、自動更新を介してそのオブジェクトのデータの変更内容を Report(報告) することができます。
基本的な使い方 ¶
PDGオブジェクトのデータにアクセスできるようにするには、まず最初に、そのオブジェクトの ビジュアライザ を作成してから、そのオブジェクトの サブスクリプション を作成する必要があります。
コード |
挙動 |
---|---|
|
PDGDを簡単に操作できるようにするユーティリティ関数とクラスが備わっています。 |
|
現行Houdiniインスタンスのデータレイヤーインターフェースを返します。 |
|
次の2つのパラメータを受け取ります:
|
サンプル
import pdgd.util data_layer = pdgd.util.get_local_data_layer() visualizer_object = pdgd.util.PDGDObject('pdg/object/path', data_layer) def my_callback(pdg_object_path, object, message): pass # この段階でvisualizer_objectが既にデータを受け取っていることに気づくことが重要です。 # その初期データを処理する必要がある場合は、一度手動でmy_callbackをコールする必要があります。 visualizer_object.addListener(my_callback)
pdgd.util.PDGDObject
クラスは、PDGDとの通信を抽象化します。
このクラスからPDGDオブジェクトを作成するとすぐに、そのオブジェクトがSubscribe(定期購読)され、そのオブジェクトの内部表現が構築され、すべてのデータがPythonタイプに変換されます。
この オブジェクトメンバー を使用することで、いつでもこの内部表現にアクセスすることができます。
さらには、Listener関数をPDGDObject
に登録することができます。
Listener関数は、そのオブジェクトのデータが変更される度に発動されます。
PDGDプロパティ名は、graph_list
やid
といった単純な文字列になっています。
将来のバージョンのPDGDと互換性を維持できるようにするために、これらのプロパティ名はpdgd.PropertyNames
を介して公開しています。
そのため、graph_list
などのプロパティ名を直接使用するのではなく、代わりにpdgd.PropertyNames.GraphList
を使用することを推奨します。
Tip
ここに基本的なPDGDの使い方を示したサンプルが見つかります: $HFS/pdgd/examples/visualizer
。
PDGグラフ ¶
現行Houdiniインスタンスで作成されたすべてのPDGグラフを追跡するハイレベルなPDGオブジェクトが存在します。
このハイレベルなPDGオブジェクトのパスがpdg
です。
サンプル
import pdgd.util data_layer = pdgd.util.get_local_data_layer() visualizer_object = pdgd.util.PDGDObject('pdg', data_layer) print(visualizer_object.object)
上記のサンプルは、graph_list
プロパティを含んだ辞書をプリントします。
このプロパティは、現行Houdiniインスタンスで作成されたすべてのPDGグラフのリストです。
新しいTOPネットワークを作成すると、このオブジェクトは、その新しいグラフがそのリストに含まれるように更新します。
graph_list
プロパティには、グラフ名のリストが格納されています。
これらのグラフ名をpdg/{graph_name}
の書式で使用することで、グラフオブジェクトパスを構築することができます。
サンプル
import pdgd import pdgd.util GRAPH_LIST_KEY = pdgd.PropertyNames.GraphList data_layer = pdgd.util.get_local_data_layer() visualizer_object = pdgd.util.PDGDObject('pdg', data_layer) graph_objects = {} if GRAPH_LIST_KEY in visualizer_object.object: for graph_name in visualizer_object.object[GRAPH_LIST_KEY]: graph_objects[graph_name] = pdgd.util.PDGDObject('pdg/' + graph_name, data_layer)
PDGノード ¶
pdg/{graph_name}/node_{node_id}
の書式を使用することで、PDGノードにアクセスすることができます。
PDGノードは、PDGグラフが所有していて、そのPDGグラフをSubscribe(定期購読)することでノードIDリストを取得することができます。
pdgd.PropertyName.NodeList
プロパティには、ノードオブジェクトパスの構築に使用可能なIDのリストが格納されています。
PDGワークアイテム ¶
pdg/{graph_name}/work_item_{work_item_id}
の書式を使用することで、ワークアイテムにアクセスすることができます。
PDGワークアイテムは、PDGグラフが所有していて、そのPDGグラフをSubscribe(定期購読)することでワークアイテムIDリストを取得することができます。
pdgd.PropertyNames.WorkItems
プロパティには、ワークアイテムオブジェクトパスの構築に使用可能なIDのリストが格納されていて、
各リストには、PDGグラフ内のすべてのワークアイテムが格納されています。
PDGノードオブジェクトにもpdgd.PropertyNames.WorkItems
プロパティがありますが、そのリストにはそのPDGノードのワークアイテムのみが格納されています。
リモートPDGD ¶
リモートPDGインスタンスからデータにアクセスするには、コードにリモートデータレイヤーインターフェースのみが必要になります。
Houdiniには、デフォルトのサーバー/クライアント実装が2つ用意されています。それがWebSocketベースの実装と(TCPソケットを使用した)バイナリ実装です。 PDGDは、データレイヤーサーバーを起動するメソッドとリモートデータレイヤーインターフェース(クライアント)を作成するメソッドを公開しています。
To... | Do this |
---|---|
サーバーを起動する |
import pdgd server_manager = pdgd.DataLayerServerManager.Instance() # 以下のメソッドの1番目の引数がサーバーのタイプで、2番目の引数がサーバーがリッスンするポート番号です。 # ゼロは、システム側が利用可能なポートを選択することを意味します。 server = server_manager.createServer('DataLayerWSServer', 0) server.serve() print('Server listening at port: {}'.format(server.getPort())) |
リモートインターフェースに接続する |
import pdgd interface_manager = pdgd.DataLayerInterfaceManager.Instance() data_layer = interface_manager.createInterface('DataLayerWSClient', 'ws://host:port') visualizer_object = pdgd.util.PDGDObject('pdg', data_layer) ローカルインターフェースを使用した場合、この import pdgd.util data_layer = pdgd.util.get_local_data_layer() visualizer_object = pdgd.util.PDGDObject('pdg/object/path', data_layer) def my_callback(pdg_object_path, object, message): pass # 手動でコールバックをコールすることで、Subscribe(定期購読)中に受信したデータを必ずチェックできるようにします。 # これは、ローカルインターフェースを使用している場合におそらく必要となります。 my_callback('pdg/object/path', visualizer_object.object, None) visualizer_object.addListener(my_callback) |
データレイヤービジュアライザ ¶
PDGDObject
クラスは、 データレイヤービジュアライザ の実装です。
通常ではPDGDObject
がたいていの使用ケースで十分ですが、もっと細かいコントロールが必要であれば、更新メッセージの制御を備えたデータレイヤービジュアライザを実装した方が良い選択肢になります。
データレイヤービジュアライザでは、必ずprocessMessage
メソッドを使用する必要があり、オプションでsubscriptionLost
メソッドを使用します。
データが変更される度にメッセージが送信され、それらのメッセージには、変更されたプロパティに関する情報が含まれています。
PDGDでのオブジェクトプロパティは常に値のコレクション(listとsetに対応)ですが、ビジュアライザではどちらか一方だけを想定してはいけません。 ローカルでのパフォーマンスの向上にはsetが使用されますが、JSONシリアライゼーションを使用してリモートデータレイヤーに接続する場合は、すべてのプロパティがlistになっています。
サンプル
import pdgd import pdgd.util class MyDataVisualizer(pdgd.DataLayerVisualizer): def __init__(self): super(MyDataVisualizer, self).__init__() def processMessage(self, message, sender): print('Received message from {}: \n{}\n'.format(sender, message.toJson())) def subscriptionLost(self): print('Subscription to data layer was lost') my_visualizer = MyDataVisualizer() data_layer = pdgd.util.get_local_data_layer() data_layer.subscribe(my_visualizer, 'pdg')
メッセージ ¶
ビジュアライザは、データにアクセスできるようにメッセージを処理します。 各メッセージは、オブジェクトのデータに起きている内容をビジュアライザに伝えます。 このメッセージは、オブジェクトのフル状態を保持した スナップショットメッセージ 、または、特定のプロパティがどのように変更されたかを示した 差分(Delta)メッセージ のどれかです。
Python APIはpdgd.Message
クラスを公開しています。
getType
メソッドをコールすることで、メッセージの タイプ を確認することができます。
返される値は以下のどれかです:
-
pdgd.Message.MessageType.eMessageTypeObjectSnapshot
-
pdgd.Message.MessageType.eMessageTypeAppendValues
-
pdgd.Message.MessageType.eMessageTypeRemoveValues
-
pdgd.Message.MessageType.eMessageTypeSetValues
toPython()
メソッドをコールすることで、すべてのメッセージデータをPythonタイプに変換することもできます。
このメソッドによって、メッセージが扱いやすくなり、さらにはC++コードのコールを少なくすることができます。
スナップショットメッセージ ¶
スナップショットメッセージには、オブジェクトのすべてのワークアイテムのプロパティとアトリビュートに関する情報が含まれています。 ほとんどのオブジェクトは、新しいサブスクリプションを追加するとすぐにスナップショットメッセージの送信を開始します。
getObjectSnapshot()
メソッドをコールすることで、pdgd.ObjectSnapshot
インスタンスを返すことができます。
差分(Delta)メッセージ ¶
差分(Delta)メッセージには、特定のオブジェクトのプロパティに関する情報とその値がどのように変更されたのかを示した情報が含まれています。
getPropertyName()
メソッドをコールすることで、どのプロパティが変更されたのかを調べることができます。
他にも、getPropertyArray()
メソッドをコールすることで、変更された値のリスト(メッセージタイプは、その値のリストの意味を決めます)を取得することができます。
set値メッセージ ¶
set値メッセージは、オブジェクトプロパティ値が置換されたことを示します。
ビジュアライザは、このメッセージ内の値リストがそのプロパティの完全な値リストとして考慮するはずです。
add値メッセージ ¶
add値メッセージは、新しい値がオブジェクトプロパティに追加されたことを示します。
このメッセージには、追加された値の値リストが含まれています。
remove値メッセージ ¶
remove値メッセージは、その値がオブジェクトプロパティから削除されたことを示します。
このメッセージには、削除された値の値リストが含まれています。
サブスクリプションハンドラー ¶
サブスクリプションハンドラーは、PDGとの通信の役割を担います。 スナップショットの構築方法、または、差分(Delta)メッセージを送信するタイミングを知っているのが、このサブスクリプションハンドラーです。
PDGDには、グラフ、ノード、ワークアイテムのサブスクリプションハンドラーが備わっていますが、独自のサブスクリプションハンドラーを記述することで、PDGDを拡張することも可能です。
PDGDは、PDGと同様にタイプシステムを使用します。
pdgd.TypeRegistry
クラスを使用することで、登録済みのサブスクリプションハンドラータイプを確認することができます。
サンプル
import pdgd type_registry = pdgd.TypeRegistry.Instance() registered_handlers = type_registry.typeNames( pdgd.registeredType.DataLayerSubscriptionHandler) print('Registered subscription handlers: {}'.format(registered_handlers))
PDGDに新しいサブスクリプションハンドラーを追加するには、PyDataLayerSubscriptionHandler
インスタンスを実装するクラスを定義して、PDGDタイプレジストリにそのクラスを登録する必要があります。
サブスクリプションハンドラーは、子サブスクリプションハンドラーを追加することができるので、データ階層を作成することができます。 例えば、PDGグラフのサブスクリプションハンドラーは、子ノードサブスクリプションハンドラーを作成します。
pdgd.util.SimpleHandlerの使い方 ¶
pdgd.util.SimpleHandler
は、新しいサブスクリプションハンドラーの作成に使用可能な基底クラスです。
pdgd.util.PDGDObject
と同様に、この基底クラスは、PDGDコードの上層にある抽象レイヤーです。
サンプル
import pdgd import pdgd.util class MySubscriptionHandler(pdgd.util.SimpleHandler): def __init__(self, handler): super(MySubscriptionHandler, self).__init__(handler) self.addValues('TestProperty', [0, 1, 2, 3, 4]) self.addValues2('TestProperty2', ["test_string"]) pdgd.util.register_new_handler(MySubscriptionHandler, 'MySubscriptionHandlerName', ' my_subscription_handler_address') data_layer = pdgd.util.get_local_data_layer() visualizer_object = pdgd.util.PDGDObject(data_layer, 'my_subscription_handler_address')
上記のサンプルでは、独自のサブスクリプションハンドラーを定義してから、pdgd.util.PDGDObject
を使用してそのハンドラーをSubscribe(定期購読)しています。
pdgd.util.register_new_handler
メソッドは3つのパラメータを受け取ります:
-
サブスクリプションハンドラー定義。
-
このタイプのハンドラーのインスタンス化に使用される名前。
-
(オプション)PDGDがそのサブスクリプションハンドラーのインスタンスを作成するパス。
コマンド ¶
データレイヤーは、双方向通信に対応しています。 サブスクリプションハンドラーは、コマンドを公開するので、あなたはそれらのコマンドを使用することで、汎用パラメータを受け取り、オプションで値を返すことができます。
サンプル
import pdgd import pdgd.util class MySubscriptionHandler(pdgd.util.SimpleHandler): def __init__(self, handler): super(MySubscriptionHandler, self).__init__(handler) @pdgd.util.SimpleHandler.command('MyCommand') def my_command(self, params): print('MyCommand command called with parameters: {}'.format(params) pdgd.util.register_new_handler(MySubscriptionHandler, 'MySubscriptionHandlerName', 'my_subscription_handler_address') data_layer = pdgd.util.get_local_data_layer() data_layer.sendCommand('MyCommand', 'Parameter', 'my_subscription_handler_address')
子ハンドラー ¶
サブスクリプションハンドラーは子ハンドラーインスタンスを持つことができます。
SubscriptionHandlerManager
インスタンスを使用することで、新しいサブスクリプションハンドラーを作成することができます。
サンプル
import pdgd import pdgd.util class MySubscriptionHandler(pdgd.util.SimpleHandler): def __init__(self, handler): super(MySubscriptionHandler, self).__init__(handler) this_handler_name = self.getObjectName() child_handler_name = 'child' child_handler_path = '{}/{}'.format( this_handler_name, child_handler_name) handler_manager = pdgd.SubscriptionHandlerManager.Instance() child_handler = handler_manager.createSubscriptionHandler(child_handler_path, 'MySubscriptionHandlerName') self.addChildHandler(child_handler_name, child_handler) pdgd.util.register_new_handler(MySubscriptionHandler, 'MySubscriptionHandlerName')
クエリシステム ¶
Queryオブジェクトは、動的な検索結果を提供する特別なオブジェクトです。
Queryオブジェクトは、Queryのパラメータを満たしたオブジェクトパスすべてを含んだ結果プロパティを持ちます。
デフォルトのデータレイヤー実装は、特別なquery?
サブスクリプションパスの後にJSONフォーマットのクエリ定義を追加することによって、クエリに対応します。
クエリの入力には、プロパティパスのリストを格納します。 クエリは、そのプロパティの値とフォーマット文字列を使用して(このフォーマット文字列の中括弧をそのプロパティ値に置換して)オブジェクトのリストを構築します。そのリストは、フィルタリングしたりソートすることができます。
Query format example
query? { 'property_paths': [ 'path/to/obj1/prop1', 'path/to/obj1/prop2', 'path/to/obj2/prop1' ], 'format_string': 'path/to/other/obj/{}', 'sort': true, 'reverse_sort': false, 'sort_property': 'SortProperty', 'filters': [ [{ 'property_name': 'filter_prop', 'filter_value': 'include_this', 'negate': false }, { 'property_name': 'filter_prop_2', 'filter_value': 'do_not_include_this', 'negate': true }] ] }
上記のサンプルでは、filters
プロパティがリストのリストになっていて、その外側のリストはAND演算を意味し、その内側のリストはOR演算を意味し、
そのフィルター定義の[[A, B, C], [D, E, F], [G, H, I]]
は、((A or B or C)and (D or E or F)and(G or H or I))
のテストに相当します。
クエリのサンプル ¶
PDGDで以下のオブジェクトが存在すると仮定します:
{ 'obj_a': { 'prop_a': [1, 3, 5] }, 'obj_b': { 'my_children_ids': [0, 1, 2, 3, 4, 5, 6, 7] }, 'obj_b/child0': { 'my_filter_prop': 0 }, 'obj_b/child1': { 'my_filter_prop': 1 }, 'obj_b/child2': { 'my_filter_prop': 2 }, 'obj_b/child3': { 'my_filter_prop': 3 }, 'obj_b/child4': { 'my_filter_prop': 0 }, 'obj_b/child5': { 'my_filter_prop': 1 }, 'obj_b/child6': { 'my_filter_prop': 2 }, 'obj_b/child7': { 'my_filter_prop': 3 } }
obj_b
の子オブジェクト内のobj_a
のprop_a
プロパティで表現されたサブセットのうち、my_filter_prop
が 1 のサブセットに興味があるのであれば、以下のクエリを構築すると良いでしょう:
import pdgd import pdgd.util query_string = """ query? { 'property_paths': [ 'obj_a/prop_a', ], 'format_string': 'obj_b/child{}', 'filters': [ [{ 'property_name': 'my_filter_prop', 'filter_value': 1, 'negate': false }] ] } """ data_layer = pdgd.util.get_local_data_layer() query_visualizer = pdgd.util.PDGDObject(query_string, data_layer)
上記のサンプルでは、クエリが他のPDGDオブジェクトと同じように動作します。 このクエリに影響を与えるデータが変更される度に、結果がどのように変更されたのかを示したメッセージが送信されます。
PDG Data Layer Panel ¶
このペインは、 New Tab > New Pane Tab Type > TOPs メニューから開くことができます。
このペインを使用することで、データレイヤーの独自のインテグレーションをテストすることができます。
PDG Data Layer Panel ペインでは以下のことができます:
-
独自のポートまたは空きポートを使用してPDGデータレイヤーサーバーを起動することができます。
-
複数のPDGデータレイヤーサーバーをスタックすることができます。
-
PDGデータレイヤーサーバーを停止することができます。
-
使用するデータレイヤーバックエンドを選択することができます。現在のところ、バックエンドは1つしか選択できません。
-
データレイヤーバックエンドの内容のJSON変更のログバージョンを作成し、それをクリップボードにコピーすることができます。 これは、デバッグ用途で役立ちます。例えば、JSONを使用して、データレイヤーの独自インテグレーションの問題が ローカル にあるのかどうか(データレイヤーのクライアントなのかサーバーなのか)を識別することができます。