# サービスの書き方

## ROSチュートリアルの流れ

1. [ROSパッケージの作り方](/project/ros_tutorial/how_to_create_pkg.md)
2. [トピックの書き方](/project/ros_tutorial/how_to_write_topic.md)
3. [独自のメッセージファイルの作り方](/project/ros_tutorial/how_to_create_msg.md)
4. [まとめて起動するやり方](/project/ros_tutorial/how_to_use_launch.md)
5. [サービスの書き方](/project/ros_tutorial/how_to_write_service.md) ←今ここ
6. [独自のサービスファイルの作り方](/project/ros_tutorial/how_to_create_srv.md)

## はじめに

この章では、サービスの書き方について説明したいと思います。

サービスにはデータを要求する「クライアント」と応答する「サーバ」があります。

これはトピックの「サブスクライバ」と「パブリッシャ」の関係に似ていますが、サービスは更に応答の**成否**を知ることができます。

またトピックではメッセージ(.msg)を使用しましたが、サービスではサービスのファイル(`.srv`)を使用します。

詳しくは[ROSでよく使用する用語](/project/ros_tutorial/appendix/ros_word.md#service)を御覧ください。

## サービスファイル

サービスでは`.srv`という拡張子になっているファイルを使用します。 まず標準のサービス`std_srvs`の`SetBool.srv`を見てみましょう。

```
rossrv show std_srvs/SetBool
```

以下のように表示されるはずです。

```
bool data
---
bool success
string message
```

`---`の上が入力、下が出力になります。

## サービスサーバ

最初にサーバ側から書いていきます。

`server.py`を作成します。

```
roscd ros_tutorial/
vim scripts/server.py
```

{% code title="server.py" %}

```python
#!/usr/bin/env python                                                           
import rospy
from std_srvs.srv import SetBool, SetBoolResponse

def callback_srv(data):
    resp = SetBoolResponse()
    if data.data == True:
        resp.message = "called"
        resp.success = True
    else:
        resp.message = "ready"
        resp.success = False
    print(resp.message)
    return resp

if __name__ == "__main__":
    rospy.init_node("srv_server")
    srv = rospy.Service('service_call', SetBool, callback_srv)
    print("ready")
    rospy.spin()
```

{% endcode %}

実行権限を与えます。

```
chmod +x scripts/server.py
```

### コード解説

```
#!/usr/bin/env python                                                           

import rospy
```

ここまで今までと同じです。

```
from std_srvs.srv import SetBool, SetBoolResponse
```

標準のサービスの`std_srvs`の中にある`SetBool`とその出力に関する`SetBoolResponse`をインポートしています。

先にメイン関数について説明します。

```
if __name__ == "__main__":
    rospy.init_node("srv_server")
```

`srv_server`というノードの名前にしています。

```
      srv = rospy.Service('service_call', SetBool, callback_srv)
```

ここでサービスをインスタンスしています。`service_call`がサービス名、`SetBool`がサービスの型、`callback_srv`がサービスの引数を返すコールバック関数名になります。

```
      print("ready")
```

一番最初は`ready`と表示させておきます。

```
      rospy.spin()
```

プログラムを終了させないようにしています。

最後にコールバック関数について解説します。

```
def callback_srv(data):
```

`callback_srv`という関数名で、受け取った値を`data`という名前にしてます。

```
resp = SetBoolResponse()
```

`SetBoolResponse`を`resp`という名前でインスタンス化しています。

```
    if data.data == True:
        resp.message = "called"
        resp.success = True
    else:
        resp.message = "ready"
        resp.success = False
```

Trueが入力された場合、つまり呼び出しがあった時、`message`にcalledを書き込み、呼び出されたことを伝えるため`success`にTrueを書き込んでいます。

それ以外の場合やFalseが入力された場合は、`message`にreadyを書き込み、呼び出されていないことを伝えるため`success`にFalseを書き込んでいます。

```
    print(resp.message)
    return resp
```

確認のため、`message`を表示しています。

最後にサービスの型を返す必要があり、`SetBoolResponse`をインスタンス化した`resp`を返しています。

## サービスクライアント

次にクライアント側を書いていきます。

`client.py`を作成します。

```
roscd ros_tutorial/
vim scripts/client.py
```

{% code title="client.py" %}

```python
#!/usr/bin/env python
import rospy
from std_srvs.srv import SetBool

if __name__ == "__main__":
    rospy.wait_for_service('service_call')
    try:
        service_call = rospy.ServiceProxy('service_call', SetBool)
        service_call(True)
    except rospy.ServiceException, e:
        print ("Service call failed: %s" % e)
```

{% endcode %}

実行権限を与えます。

```
chmod +x scripts/client.py
```

### コード解説

```
#!/usr/bin/env python                                                           
import rospy
```

ここまで同じです。

```
std_srvs.srv import SetBool
```

`SetBool`型をインポートしています。

```
    rospy.wait_for_service('service_call')
```

ここで`service_call`というサービスが立ち上がるのを待ちます。

トピックを違い、サービスは一対一通信のため相手がいないとエラーを吐きます。 そのためサービスの立ち上がりを待っています。

```
    try:
        service_client = rospy.ServiceProxy('service_call', SetBool)
        service_client(True)
    except rospy.ServiceException, e:
        print ("Service call failed: %s" % e)
```

`sarvice_call`という名前の`SetBool`型のサービスプロキシ`service_client`を作成しています。

`service_client`にTrueを与えています。

## 実行方法

それぞれ別のターミナルで実行してください。

```
roscore
```

```
rosrun ros_tutorial server.py
```

```
rosrun ros_tutorial client.py
```

## 実行結果

```
rosrun ros_tutorial client.py
```

を実行した時、`rosrun ros_tutorial server.py`を実行しているターミナルで、

```
called
```

と表示されたら、正常に動作しています。

## 別の方法

```
roscore
```

```
rosrun ros_tutorial server.py
```

ここまで同じですが、こちらでは`rosservice`というコマンドを使用します。 このコマンドでもサーバに要求できます。

```
rosservice call /service_call "data: true"
```

```
rosservice call /service_call "data: false"
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://raspimouse-sim-tutorial.gitbook.io/project/ros_tutorial/how_to_write_service.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
