HAL version 1.4 からはディレクトリ構成が見直され、システムのコア部分がユーザーファイルから分離されました。

より構築が簡単になったソケットサーバー「HAL」を、ぜひご体験ください。

※この記事で紹介しているソケットサーバー『HAL』は、諸般の事情により現在、公開と販売を中止しています。この製品へのお問い合わせは画面上部の『お問い合わせ』メニューよりご連絡ください。

※この記事は執筆・公開から3年以上経過しています。記事の情報が古くなっている場合がありますのでご注意ください。

公開日時:2016/08/11 10:06
最終更新:2019/12/10 03:29

HALのファイル・ディレクトリ構成(version 1.4 以降)

HAL version 1.4 からはディレクトリ構成が見直され、システムのコア部分がユーザーファイルから分離されました。

これは、今後のアップグレードにあたって、コア部分のファイルを差し替えるだけで済むようにすることを目指しています。

2016/08/30 追記 version 1.4.1 ではディレクトリ構造にそれほど大きな変更はありません。主な変更点は Julius の認識文法ファイル生成が簡単になった事、多言語対応へのアプローチが行われた事、常時稼働での DataMapper Lite の不具合の解消等です。以下、濃いブルーで記述された文章は version 1.4.1 での追加・変更です

外観(第一階層)

第一階層

1. actions

このディレクトリ内には、音声認識ライブラリ「Julius」からの認識結果と、WEB アプリなどからの TCP/IP ソケット経由での命令についての定義ファイル(YAMLファイル)、及び、それらの定義で実行されるクラスファイルが格納されます。

ディレクトリ構成は下図のようになっています(それぞれ中身はサンプルファイルであり、自由に追加・変更できます)。

version 1.4

actions 1.4

version 1.4.1

actions 1.4.1

このように、julius ディレクトリと my ディレクトリ、そして juliusActions.yml と myActions.yml があります。

まず、juliusActions.yml を見てみます。

juliusActions.yml

## Julius Action YAML.
---
(省略)

-
    type:   "ハル聞こえる"
    do:     "System::can_you_hear_me"
-
    type:   "プレイリストを?取り込み"
    do:     "Playlist::make"
-
    type:   "\\A(.+)再生\\z"
    do:     "Play::music"

(省略)

上記のような記述になっています、これは YAML(ヤムル)というデータ記述形式で、インデント(字下げ)でデータ構造を記述する、とても便利な記述形式です。もともとは C、Python、Perl といった言語で利用されていた形式のようで、PHP ではデフォルトでは利用できませんが、pecl を使うことで簡単に YAML エクステンションを導入できます。

参考)PHP の YAML 拡張のインストール

上記の type で記述されているのは、音声認識ライブラリ Julius から渡された認識結果に対する「正規表現」でのマッチングパターンです。

参考)サルにもわかる正規表現入門

この YAML に記述されたパターンを上から下へと順番に検査していき、パターンにマッチした場合、do に記述子てあるクラスメソッドを実行します。例えば \A(.+)再生\z は、「ほにゃらら再生」という言葉に対してマッチし、結果として、Play クラスの music メソッドを実行します。

この Play クラスは、/actions/julius/ ディレクトリの中に配置されることが想定されていて、サンプルコードでは、そこに Play.php ファイルが存在します。

次は一例として Play.php ファイルの中身を見てみましょう。

Play.php

<?php
namespace Feijoa\HAL;

class Play
{
    public static function music(HALDto $dto, $read_sock, $args)
    {
        $match = false;

        foreach($dto->play_list as $music)
        {
            if($music[0] === $args[0])
            {
                $match = true;
                $filename = str_replace(".", "_", $music[1]);
                Voice::say($dto, $filename, "{$music[0]}を再生してもよろしいですか");
                $dto->asc_limit = time() + HAL::ASK_LIMIT;
                $dto->play_target = $music[1];
                break;
            }
        }

        if($match === false){
            Voice::say($dto, "unregistered_music", "登録されていない曲です");
        }
    }
}

このように、Play クラスが定義されていて、そのメンバ変数として music() メソッドが定義されています。

ソケットサーバー「HAL」での Julius からの命令の処理では、第一引数には「HAL」内で利用されるデータコンテナである HALDto クラスオブジェクトの $dto が渡されます。第二引数は「HAL」に接続している TCP/IP ソケットが渡されます。Julius からの命令では常に、/HAL/system/voice_client.php 内からの接続ソケットが渡されます。

特筆すべきは第三引数 $args です。

この変数には、juliusActions.yml の type で指定した正規表現のうち、( ) を使ったグループの結果が配列として返ります。

例えば、"(.+)は(.+)ね" という type の場合、「今日は暑いね」や「明日は晴れだね」等、様々な言葉がマッチしますが、この場合に第三引数に渡されるのはそれぞれ、

array("今日", "暑い")

array("明日", "晴れだ")

となります。つまり、この $args を調べることで、do で指定されたクラスメソッド内での処理を分岐させることが出来ます。この配列の要素は任意であり、type で指定した正規表現に示された ( ) の個数分だけ作成されます。(※利用可能な最大個数の制限のリファレンスが見つかりませんでした。

なお、この yml の設定では type、及び do の他に、mod と namespace の指定が可能です。

mod は、PHP の preg 関数で利用できる PCRE 修飾子が利用できます。省略された場合、デフォルトで iu (英字の大文字と小文字を区別しない、UTF-8でのマッチングを行う)になります。

namespace は、PHP での namespace の指定ができます。省略された場合、デフォルトでは Feijoa\HAL\ が利用されます。(※YAML で設定した文字列をさらに preg 関数で利用するため、namespace の区切りは \\ のようにエスケープが必要になることに注意しましょう)

なお、サンプルコードでは沢山の処理が既に記述されているため、ステップ・バイ・ステップで HAL を育てていくには向かないかもしれません。この場合は yml ファイルの既存コードを削除し、更に必要であれば julius ディレクトリ内のファイルを削除して下さい。

次は、myActions.yml を見てみます。

myActions.yml

## My Action YAML.
---
-
    type:   "light"
    do:     "MyLight::turn"

myActions.yml も、記述方法は juliusActions.yml と同じです。

myActions は WEB アプリ等から「HAL」に接続して命令を行う場合の機能です。このため正規表現ではなく、HAL, で始まるカンマ区切りの直接的な命令を受け付けるようになっています。

例えば、

HAL,light,on

という命令が送られると、上記 myActions.yml の定義に従って、MyLight クラスの turn メソッドが実行され、第一引数に HALDto クラスオブジェクトである $dto、第二引数に 接続してきた WEB アプリのソケット、第三引数に array("on") が渡されます。この第三引数を検査して MyLight::turn() メソッド内で分岐が行えるのは juliusActions の時と同じです。

なお、この myActions 用の クラスファイルは /actions/julius/ ディレクトリ ではなく、 /actions/my/ ディレクトリ内に置くのを推奨します。

上記 /actions/julius/ ディレクトリと /actions/my/ ディレクトリは /setting/requires.php 内で定義されている通りオートロードの対象になっているので、各 action クラスを個別に require する必要はありません。クラス名と同じ名前のファイル名で保存して、各ディレクトリ内に配置して下さい。また、namespace の概念は少し難しくなってしまうため、現在、namespace を利用してはいますが Feijoa\HAL\ に限定しており、HAL 内で共通の namespace です。このため、/HAL/system/class/ 内にあるクラスファイルと同じファイル名のクラスは利用しないで下さい。

2. apps

HAL から利用されるわけではないが、関連するアプリケーションを格納するために用意されたディレクトリです。例えば、cron で実行する室温計測アプリ等をこのディレクトリに入れておき、そこで計測されたデータを HAL で利用する、等の利用方法が考えられます。

3. sample

このディレクトリはサンプルデータが格納されています。このディレクトリの中身はリリース毎に変わるでしょう。

4. setting

このディレクトリは、あなたの環境に合わせた「HAL」の設定を格納するディレクトリです。

vesrion 1.4

setting

version 1.4.1

setting

PlaySound.php

音声出力に関するメソッドが纏められています。お使いの Raspberry Pi のデバイス設定状況、インストール済みアプリケーション等に合わせて適宜、追加・変更して下さい。

HALSetting.php

HAL に関する設定が記述されています。各値の内容については添付してあるコメントを参照して下さい。

version 1.4.1 では、いくつかの設定項目が追加されています。ご注意ください。>/span>

HALDto.php

HAL 内で常に利用されるコンテナオブジェクト用のクラスです。HAL の処理では常に、このオブジェクトが受け渡されます。基本的には全て public な変数を設定し、読み出しを行うことを想定しています。

何らかの処理のタイマーやフラグは、このオブジェクトを利用して受け渡されることが想定されています。

requires.php

HAL の実行に必要なクラスファイルの読み込みを行っています。オートロード機能もあり、必要な時に必要なクラスが読み込まれます。

SetUp.php

HAL の require ファイルが準備され、HALDto クラスオブジェクトが生成されて初期化された後に呼び出されます。このファイル内で、使用する GPIO のセッティング等を行って下さい。

この処理が終わると、HAL は 自分自身に対してTCP/IPソケットの接続を試みます。

Ready.php

TCP/IPソケットによる、自分自身への接続も確立され、Julius からの音声認識結果と WEB アプリなどからのソケット経由での命令を待つ準備が整った段階で実行されます。この Ready::exec() の直後から、各命令の待受けが開始されます。

DBSetting.php

HAL で利用するデータベースの設定です。設定方法については、DBアクセスライブラリ「DataMapper Lite」を御覧ください。もし、データベースをご利用に成らない場合は SetUp.php 内の以下の記述をコメントアウトするか、削除してください。

// DBのセットアップ
$dto->DB = DBSetting::getDB();
$dto->pdo = DBConnect::getInstance()->getPDO($dto->DB);
DataMapper::getInstance()->db($dto->DB);

また、サンプルコードである /actions/julius/Temp.php の、getTempAvgYesterday() メソッドの最初に

private static function getTempAvgYesterday()
{
    return null;

(以下略)

として、 null を return させることで、エラーを出さずに試用が可能です。

なお、この Temp::getTempAvgYesterday() メソッドは、前日の同時刻前後 15 分間の計測温度の平均を求めて返す関数です。

CleanUp.php

HAL を終了する場合の処理を、この CleanUp::exec() 内に記述子ます。Open JTalk を使った「ハルを、終了します」といった音声メッセージなど、必要と思われる処理はここに記述して下さい。

5. system

このディレクトリ内には、ソケットサーバー「HAL」のコアファイルが格納されています。このディレクトリ内の実行ファイルは全て暗号化されており、お客様は変更できません。

system

ただし、/HAL/system/class/ 内の I18N.php 及び Voice.php は多言語対応中であり、暫定プログラムのため暗号化していません。ご使用のアプリケーションに合わせて適宜、追加・変更してください。

今後のリリースにあたっては、基本的にこのディレクトリの置き換えのみで利用できるように開発を進めていく予定です。

なお、現在、flags ディレクトリには、Julius を制御するためのロックファイル等が書き出される仕様になっています。書き出すファイルは 2 バイトのファイルですが、頻繁に作成・削除されるため、今後、メモリキャッシュシステムを必須にすることで、ファイルの読み書きを止めるかもしれません。

templates ディレクトリは現在、DataMapper Liteで利用されている DataModel 用のテンプレートファイルのみです。今後、Julius の音声認識用テンプレートファイルなども格納される予定です。

6. userdata

このディレクトリは、お客様固有のデータが格納されます。

version 1.4

userdata

version 1.4.1

userdata

languages ディレクトリ

このディレクトリには言語ロケール毎のメッセージを定義した YAML ファイルを格納します。

例)ja_JP.yml

## Voice: Japanease Tokyo
---
# system command
quit:           "ハルプロセスを終了してもよろしいですか?"
shutdown:       "システムをシャットダウンしてもよろしいですか?"
reboot:         "システムを再起動してもよろしいですか?"
hal_shutdown:   "ハルを、終了します"    
hal_started:    "ハルを、起動しました"
do_reboot:      "ラズベリーパイを再起動します"
do_shutdown:      "ラズベリーパイをシャットダウンします"
unknown_command:    "定義されていないコマンドです"
select_language:    "日本標準語にします"
bye_bye:            "さようなら"
i_ll_be_back:       "しばらくお待ち下さい"
see_you_later:      "また後ほどお会いしましょう"
cancel:             "キャンセルします"

# user command
stop_music:     "再生を停止しました"
play_has_done:  "再生を終了しました"
welcome_back:   "おかえりなさいませ"
welcome_back_early:  "おかえりなさい。お早いお帰りですね"
welcome_back_i_did_not_know:    "お出かけでしたか。おかえりなさい"
noodle_is_cooked:   "カップラーメンが出来上がりました。お召し上がり下さい"
did_you_call_me:    "はい、呼びましたか?"

(中略)

whats_the_date_today:        ":@1月:@2日:@3です"
what_time_is_it_now:         ":@3 :@1時 :@2分です"
no_janle_find:                ":@1のニュースは見つかりません"
news_about:                   ":@1のニュースをピックアップしますか?"
no_news:                      "ニュース:@1番は存在しません"
read_up:                      "ニュース:@1番を読み上げます"
noodle_timer:               "カップラーメンタイマーを:@1分に設定しました。しばらくお待ちください"
temp_def_low:               ":@1度低いようです"
temp_def_hight:            ":@1度高いようです"
now_temp:                   "只今の温度は:@1度です"

上記のように、キー: メッセージという1対1の定義を行います。上記例の下部にある :@1 といった表記は、パラメター置換え用のプレースホルダーです。HAL で発声を行うには Voice クラスの say() メソッドをりようしますが、

Voice::say($dto, "whats_the_date_today", "8月30日火曜日です", array(8, 30, "火曜日"));

のように第 4 引数に配列を渡すことで、n 番目のプレースホルダー :@n を配列内の要素と置き換えを行えます。第 2 引数が利用するメッセージのキーであり、これに対応する YAML のメッセージが利用されますが、対応するメッセージが定義されていない場合は 第 3 引数で渡した文字列が発声されます。ブレースホルダー番号は正規表現に似せて 1 オリジンとしました。

なお、第 5 引数に配列を渡すことで、.wav ファイル名の suffix が指定できます。例えば

Voice::say($dto, "whats_the_date_today", "8月30日火曜日です", array(8, 30, "火曜日"), array(8, 30, "火曜日"));

のようにすると、voice ディレクトリ内には

whats_the_date_today_8_30_火曜日.wav
whats_the_date_today_8_30_火曜日.txt

というファイルが作成されます。

7. juliusActions.php

version 1.3 までは、Julius からの音声認識結果をこのファイル内で処理していました。version 1.4 からは /actions ディレクトリ内にファイルを定義することで処理を行いますが、以前と同様にこのファイル内に処理を記述することも出来ます。(推奨されません)

8. myActions.php

version 1.3 までは、WEB アプリ等からの音声認識結果をこのファイル内で処理していました。version 1.4 からは /actions ディレクトリ内にファイルを定義することで処理を行いますが、以前と同様にこのファイル内に処理を記述することも出来ます。(推奨されません)

9. Sensor.php

Raspberry Pi に接続したセンサーの認識結果の処理を記述することを想定しています。このクラスのメソッド Sensor::exec() は /HAL/setting/HALSetting.php 内に記述されている HAL::SYSTEM_INTERVAL マイクロ秒毎 に呼び出されます(CPU負荷により遅延します)。

ソケットサーバー「HAL」には、12bit AD コンバーター(MCP3208-CI/Pを想定)の認識結果を読み取るクラス ReadADC が同梱されており、この認識メソッド ReadADC::exec() メソッドは、HAL 内のプログラムのどこからでも呼び出しができます。

なお、ReadADC::exec() の第一引数は読み取りチャンネルで、第二引数は読み取り結果の形式であり、省略または false を指定した場合は 0~100% までの実数、true を指定した場合は 0~4095 の整数となります。

また、HAL::SYSTEM_INTERVAL マイクロ秒毎 に呼び出される特性を利用して、タイマーなどで行う処理も、この Sensor.php 内に記述することが想定されています。PHP ではデフォルトではスレッドが利用できないため、HAL 内を常に循環している HALDto クラスオブジェクトである $dto にタイマー時刻をセットし、その時刻を過ぎているかを検査してそれをトリガーにするのが得策です。

記事リンク