Pepperのファイル探訪(プロジェクトファイル)

Pepperの開発ツールChoregrapheにおいて、プロジェクト単位で共同作業しようとか考え始めると、これらのファイルの形式や内容が知りたくなってきます。
特に、GitHubとかそういうところでバージョン管理、共同編集しようとすると、差分がどう見えるのかが重要になってくるので、テキストだったらいいなとか思うわけです。

ドキュメントを読んでも、あまり形式については詳しく言及されていなかったのでざっくり見てわかる範囲で調べてみたので簡単に書いてみます。今回はプロジェクトファイルをお題に。

結論としては、**XMLをベースにファイルが構成されているので、GitHubとかでもうまく差分が見えて、比較的気持ちよく管理できるかも**、といった感じ。


#プロジェクトファイル

Choregrapheでプロジェクトを保存すると、以下のような感じの構成のファイルができる。

* `(プロジェクト名)フォルダ`
    * `(プロジェクト名).pml`
    * `manifest.xml`
    * `.metadata`
    * `(ビヘイビア名)フォルダ`
        * `behavior.xar` 

基本的には、[プロジェクトの内容]パネルで見えているファイル構成ができている雰囲気。

###(プロジェクト名).pml

プロジェクトファイルの基点となるファイルで、この拡張子のファイルを開くと関連付けられているChoregrapheが起動する。
*mlなのでXMLだろうと思って開くとこんな感じ。

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<Package name="HogeHoge" format_version="4">
    <Manifest src="manifest.xml" />
    <BehaviorDescriptions>
        <BehaviorDescription name="behavior" src="behavior_1" xar="behavior.xar" />
    </BehaviorDescriptions>
    <Dialogs />
    <Resources />
    <Topics />
</Package>
```

これなら、リソース追加したとか、そういった差分が簡単にわかりそう。

###manifest.xml

拡張子の通りでXML。

```xml
<?xml version='1.0' encoding='UTF-8'?>
<package uuid="hogehoge-ee8dd3">
 <names>
  <name lang="en_US">Hogehoge</name>
 </names>
 <supportedLanguages>
  <language>en_US</language>
 </supportedLanguages>
 <descriptionLanguages>
  <language>en_US</language>
 </descriptionLanguages>
 <contents>
  <behaviorContent path="behavior_1">
   <nature>interactive</nature>
   <permissions/>
  </behaviorContent>
 </contents>
</package>
```

プロジェクトのプロパティで設定しているような内容が入っている。ドキュメントによると、Storeにアップロードする際に必要となるような種類の情報っぽい。

###.metadata

自分が作成したプロジェクトでは、空のファイルだった。ドキュメントにも言及なし？

###behavior.xar

拡張子的に、何か圧縮されたアーカイブかな・・・と思ってバイナリエディタで開いてみるも、これもXML。
フローダイアグラムで書いていた内容が、ボックスの内容合わせてXMLとして保存されている感じで、これも変更があれば差分で見えてきそう。

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<ChoregrapheProject xmlns="http://www.aldebaran-robotics.com/schema/choregraphe/project.xsd" xar_version="3">
    <Box name="root" id="-1" localization="8" tooltip="Root box of Choregraphe&apos;s behavior. Highest level possible." x="0" y="0">
        <bitmap>media/images/box/root.png</bitmap>
        <script language="4">
            <content>
                <![CDATA[]]>
</content>
        </script>
        <Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when diagram is loaded." id="1" />
        <Input name="onStart" type="1" type_size="1" nature="2" inner="0" tooltip="Box behavior starts when a signal is received on this input." id="2" />
        <Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Box behavior stops when a signal is received on this input." id="3" />
        <Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="ボックス動作の終了時に信号を送る。" id="4" />
        <Timeline enable="0">
            <BehaviorLayer name="behavior_layer1">
                <BehaviorKeyframe name="keyframe1" index="1">
                    <Diagram>
                        <Box name="Say" id="1" localization="8" tooltip="Say some text. Note that you must open the box to enter the text." x="121" y="17">
                            <bitmap>media/images/box/interaction/say.png</bitmap>
                            <script language="4">
                                <content>...</content>
                            </script>
                            <Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when Diagram is loaded." id="1" />
                            <Input name="onStart" type="1" type_size="1" nature="2" inner="0" tooltip="Box behavior starts when a signal is received on this Input." id="2" />
                            <Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Box behavior stops when a signal is received on this Input." id="3" />
                            <Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="Signal sent when box behavior is finished or stopped." id="4" />
                            <Parameter name="Voice shaping (%)" inherits_from_parent="0" content_type="1" value="100" default_value="100" min="50" max="150" tooltip='...' id="5" />
                            <Parameter name="Speed (%)" inherits_from_parent="0" content_type="1" value="100" default_value="100" min="50" max="200" tooltip="..." id="6" />
                            <Timeline enable="0">
                                <BehaviorLayer name="behavior_layer1">
                                    <BehaviorKeyframe name="keyframe1" index="1">
                                        <Diagram>
                                            <Box name="Say Text" id="2" localization="8" tooltip="Say the text received on its input." x="422" y="65">
                                                <bitmap>media/images/box/interaction/say.png</bitmap>
                                                <script language="4">
                                                    <content>
                                                        <![CDATA[import time

class MyClass(GeneratedClass):
    def __init__(self):
        GeneratedClass.__init__(self, False)
        self.tts = ALProxy('ALTextToSpeech')
        self.ttsStop = ALProxy('ALTextToSpeech', True) #Create another proxy as wait is blocking if audioout is remote

    def onLoad(self):
        self.bIsRunning = False
        self.ids = []

    def onUnload(self):
        for id in self.ids:
            try:
                self.ttsStop.stop(id)
            except:
                pass
        while( self.bIsRunning ):
            time.sleep( 0.2 )

    def onInput_onStart(self, p):
        self.bIsRunning = True
        try:
            sentence = "\RSPD="+ str( self.getParameter("Speed (%)") ) + "\ "
            sentence += "\VCT="+ str( self.getParameter("Voice shaping (%)") ) + "\ "
            sentence += str(p)
            sentence +=  "\RST\ "
            id = self.tts.post.say(str(sentence))
            self.ids.append(id)
            self.tts.wait(id, 0)
        finally:
            try:
                self.ids.remove(id)
            except:
                pass
            if( self.ids == [] ):
                self.onStopped() # activate output of the box
                self.bIsRunning = False

    def onInput_onStop(self):
        self.onUnload()]]>
</content>
                                                </script>
                                                <Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when Diagram is loaded." id="1" />
                                                <Input name="onStart" type="3" type_size="1" nature="2" inner="0" tooltip="Box behavior starts when a signal is received on this Input." id="2" />
                                                <Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Box behavior stops when a signal is received on this Input." id="3" />
                                                <Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="Signal sent when Box behavior is finished." id="4" />
                                                <Parameter name="Voice shaping (%)" inherits_from_parent="1" content_type="1" value="100" default_value="100" min="50" max="150" tooltip='...' id="5" />
                                                <Parameter name="Speed (%)" inherits_from_parent="1" content_type="1" value="100" default_value="100" min="50" max="200" tooltip="..." id="6" />
                                            </Box>
                                            <Box name="Localized Text" id="5" localization="8" tooltip="..." plugin="localizationbox_plugin" x="114" y="68">...</Box>
                        <Link inputowner="1" indexofinput="2" outputowner="0" indexofoutput="2" />
                    </Diagram>
                </BehaviorKeyframe>
            </BehaviorLayer>
        </Timeline>
    </Box>
</ChoregrapheProject>
```

このフォーマットはすごくシンプルでよいのだけど、Say Textボックスのスクリプトがそのまま書かれているのを見る限り、ボックスライブラリからボックスを配置することで、ボックスのコピーが作成されるということなのだなー・・・

ボックスライブラリのアップデートがあった場合に、アップデートされたボックスを使用しているアプリケーション側がどうそのアップデートに追従するか、というのはちょっと考えておいたほうがよいのかもしれない。

こんな感じで、今回はプロジェクトファイルをみてみましたが、GitHubでも差分はおおむねテキストで見えそうなのでバージョン管理、共同作業だなと。
次は、ボックスライブラリファイルについてもみてみたいなとか考え中。またいずれ！