Typhoon HIL RPC API

RPC(リモート・プロシージャ・コール)は、2つの独立したプロセス間の通信を可能にするプロセス間通信技術です。RPCは、次のような分散クライアント/サーバー・プログラムの作成に最もよく使用されます。

  • クライアントは、別のプログラムによって提供されるサービスを要求するプロセス(プログラムまたはタスク)であり、

  • サーバーは、サービスを提供する(クライアントからの要求に応答する)プロセス(プログラムまたはタスク)です。

Typhoon HILのRPC APIを使用すると、ZMQ [ 1 ]ライブラリでサポートされている限り、任意の言語でカスタムAPIを記述できます。

メッセージフォーマット

クライアントとサーバー側の間で交換されるメッセージはJSON-RPCプロトコル[ 2 ]と互換性があります。

要求メッセージには次のメンバーが含まれます。

  • api - Typhoon HIL RPC API のバージョンを指定する文字列。

  • jsonrpc - JSON-RPCプロトコルのバージョンを指定する文字列。正確に「2.0」でなければなりません。

  • method - 呼び出されるメソッドの名前を含む文字列。

  • params - メソッドの呼び出し時に使用されるパラメータ値のリストまたは辞書。このメンバーはオプションです。

  • id - クライアントによって確立された識別子。文字列または整数のいずれかになります。

応答メッセージには次のメンバーが含まれます。

  • jsonrpc - JSON-RPCプロトコルのバージョンを指定する文字列。正確に「2.0」でなければなりません。

  • result - このメンバーは、メソッドが呼び出され、正常に終了した場合にのみ存在します。

  • error - このメンバーは、メソッドの呼び出しまたは実行中にエラーが発生した場合にのみ存在します。

  • 警告- このメンバーは、メソッド実行中に警告が発生した場合にのみ存在します。

  • id - リクエスト内の id メンバーの値と同じである必要がある識別子。

メッセージ形式の詳細についてはJSON-RPC仕様[ 3 ]を参照してください。

使用法

Typhoon HIL RPC API は次の API に使用できます。

利用可能なメソッドの全リストについては、前述のAPIのドキュメントをご確認ください。各APIは異なるポート番号を使用します。ポート番号は、アナウンスポートをサブスクライブすることで取得できます。アナウンスポート番号は、APIバージョン固有のフォルダ(%APPDATA%\typhoon-api)にあるsettings.confファイルで定義された範囲から取得されます(APIバージョン1.7.0の場合は、%APPDATA%\typhoon-api\1.7.0)。

次の例は、Typhoon HIL RPC API の使用方法を示しています。

例1

この例では、Typhoon API のポート番号を取得する方法を示します。

import zmq
SERVICE_REGISTRY_HEADER = "typhoon-service-registry"


def discover(start_port=50000, end_port=50100, req_retries=30, timeout=1000):
    """Discovers port numbers for each of Typhoon APIs, in a given range:
    (start_port, end_port).

    Note: Check if the default param values match values in your settings.conf.

    Args:
        start_port (int): defines start of the port discovery range
        end_port (int): defines end of the port discovery range
        req_retries (int): defines number of times function will try to reach
            Typhoon API server
        timeout (int): defines timeout between each request retry, in
            milliseconds.

    Returns:
        dict
    """
    context = zmq.Context()
    socket = context.socket(zmq.SUB)

    offset = end_port - start_port + 1

    for i in range(offset):
        port = start_port + i
        socket.connect(f"tcp://localhost:{port}")

    socket.setsockopt_string(zmq.SUBSCRIBE, "")
    socket.setsockopt(zmq.LINGER, 0)

    poller = zmq.Poller()
    poller.register(socket, zmq.POLLIN)

    while req_retries != 0:

        socks = dict(poller.poll(timeout))
        if socks.get(socket) == zmq.POLLIN:
            response = socket.recv_json()

            if not response:
                break

            result = response.get("result")

            # Continue discovery if a received message is not from Typhoon
            # HIL Control Center.
            header = result[0]
            if header != SERVICE_REGISTRY_HEADER:
                continue

            return result
        else:
            req_retries -= 1
            if req_retries == 0:
                break

    raise RuntimeError("Server not found.")

例2

この例では、ロードメソッドを呼び出す方法を示します。

import zmq

context = zmq.Context()
req_socket = context.socket(zmq.REQ)
# Connect with the server
# In this example we assume that the server listens on the port 51357.
# Example 1 shows how to always get correct port number.
req_socket.connect("tcp://localhost:51357")

# Request message
message = {
    "api": "1.0",
    "jsonrpc": "2.0",
    "method": "load",
    "params": {"filename": "abs_path_to_the_model"},
    "id": 1
}

req_socket.send_json(message)
response = req_socket.recv_json()

例3

この例では、コンパイルメソッドを呼び出す方法を示します。

import zmq

context = zmq.Context()
req_socket = context.socket(zmq.REQ)
# Connect with the server
# In this example we assume that the server listens on the port 51357.
# Example 1 shows how to always get correct port number.
req_socket.connect("tcp://localhost:51357")

# Request message
message = {
    "api": "1.0",
    "jsonrpc": "2.0",
    "method": "compile",
    "params": {},
    "id": 1
}

req_socket.send_json(message)
response = req_socket.recv_json()

例4

この例では、複数のリクエストを一度に送信する方法を示します。

import zmq

context = zmq.Context()
req_socket = context.socket(zmq.REQ)
# Connect with the server
# In this example we assume that the server listens on the port 51357.
# Example 1 shows how to always get correct port number.
req_socket.connect("tcp://localhost:51357")

# Request message
message = [
    {
        "api": "1.0",
        "jsonrpc": "2.0",
        "method": "load",
        "params": {"filename": "abs_path_to_the_model"},
        "id": 1
    },
    {
        "api": "1.0",
        "jsonrpc": "2.0",
        "method": "compile",
        "params": {},
        "id": 1
    }
]

req_socket.send_json(message)
response = req_socket.recv_json()
print("Great success!")