Webプログラマの夏休み PHPでNゲージ模型を自由自在に動かそう[動画つき]

第4回レイアウトエディタ作りました

さっそくですが、前回予告したレイアウトエディタです。Webでレイアウトを入力するということで、ちょっとがんばって、いわゆるAjaxライクなシステムにしてみました。組込みが好きな方には申しわけないのですが、今回はあまり組込みの話は出てきません。

では、まずこれを使ってレイアウトを入力してみましょう。

インストール

動作条件ですが、シミュレータと同じで、PHP4またはPHP5と、gdライブラリが必要です。また、今回はブラウザ側がJavaScriptとSVGをサポートしている必要があります。当方では、FirefoxとOperaで動作を確認しています。

最初に、レイアウトエディタのファイルを以下からダウンロードしてください。

前回のシミュレータと同様、ダウンロード、解凍したeditrail.phpとrailconf.txtをWebサーバからアクセスできるディレクトリに置きます。入力途中の情報が、raliconf.txtに書き込まれていきますので、chmod a+rw railconf.txtなどとして、railconf.txtにWebサーバから書き込めるように設定します。

次に、editrail.phpの先頭にある$urlを、WebサーバにアクセスするときのURLと同じになるように設定します(これは、POSTを受け取ったときにLocation:でリダイレクトさせ、履歴の問題を回避するのに使用しています⁠⁠。

シミュレータも使用する場合は、simrail.phpを同じディレクトリに置くと、railconf.txtが共有されますので、作成したレイアウトをすぐにシミュレートすることができます。ただし、このときrailconf.txtがシミュレータによって書き換えられてしまいますので、よく理解した上で使用してください。

editrail.phpをブラウザから開いて、レイアウト画面が表示されれば、インストールは成功です。

図1 レイアウトエディタの画面
図1 レイアウトエディタの画面

レールレイアウトを入力する

では、さっそくレイアウトを入力してみましょう。作るのは、第1回のときに使用した以下のレイアウトです。あらかじめrailconf.txtをエディタで開いて、内容を空にしておいてください。

図2 作成するレイアウト
図2 作成するレイアウト

レイアウトエディタは画面の左側がツールバーになっています。いちばん上の行にある2つのツールのうち、左側が選択ツールです。起動したときはこちらが選択されています。

図3 レイアウトエディタのツールバー
図3 レイアウトエディタのツールバー

レールを入力するときは、右側のレールツールを使用します。まず、右下の直線部分を入力してみましょう。以下のように操作してください。

  1. レールツールをクリックします。
    →レールツールが選択されます。
  2. 画面右側のレイアウトエリアで、ドラッグ操作をします。
    →入力したレールが表示されます。

入力したいレールの始点から終点までドラッグすると、レールが入力されます。向きがありますので、注意してください。今回の場合、右から左にドラッグします。入力した向きに矢が表示されますので、確認できます。

もしここでWebサーバのエラーが表示されてしまう場合は、ファイルの書き込みが可能であるか、$urlの設定が正しいかどうかを確認してください。また、ドラッグしたときにラバーバンドが表示されない場合は、ブラウザがSVGやJavaScriptをサポートしていない可能性があります。エラーコンソールなどを開いて、エラーが出ていないかを確認してみてください。

配線

使用しているコントローラには回路が14組あり、これをレールに配線して使っています。どの番号の回路をつなぐかを、ツールバーの左側にある給電パレットで選択します。⁠00」などの数字をクリックしてからレールを入力すると、その「00」に接続されたレールが入力されます。さきほど入力したレールは、初期状態の「00」になっているはずです。

では、左側の半円部分を入力してみましょう。このレールにつなぐのは3番の回路ですので、⁠03」のところをクリックしてから入力します。

  1. レールツールをクリックします。
    →レールツールが選択されます。
  2. 「03」をクリックします。
    ⁠03」が選択されます。
  3. レイアウトエリアでドラッグ操作を行います。
    →レールが入力されます。

このレイアウトエディタでは直線しか入力できませんので、半円は直線の並びとして入力する必要があります。直線どうしは端点が一致していれば、シミュレーション時につながっているものとみなされます。

ところで、このままではどのレールが何番か見分けがつきませんね。こんなときは、⁠03」の横にある「+」のスイッチをクリックするとレールに給電が行われ、その番号のレールの色が変わります。クリックするたびに「A」⁠オフ:白⁠⁠→⁠D」⁠プラス:赤⁠⁠→⁠d」⁠マイナス:青)と切り替わり、レールにも色がついて確認できるようになっています。なお、(マイナス)のときだけは、レールの矢の向きが反対になりますので、給電パターンから車両の走行方向を読み取れるようになっています。

入力後に変更するには?

すでに入力したレールの属性を変更することも可能です。ツールバーの選択ツールをクリックし、変更したいレールをクリックします。このとき、選択されたレールに接続されている回路が、給電パレットに表示されます。

この状態で「00」⁠01」などをクリックすると、選択されたレールの回路を変更することができます。複数のレールを一度に変更するには、レイアウトエリアでドラッグ操作を行うと、ドラッグ枠の中に入ったレールがすべて選択されますので、この状態で給電パレットをクリックします。

レールを選択したあと、選択枠をドラッグすることでレールを移動することもできます。また、レールを1つだけ選択したときはレールの始点と終点に変形ハンドルが表示されますので、これをドラッグすることでレールの形を変えることができます。

選択ツールの下にあるのは、削除ツールです。これをクリックすると、選択したレールを削除することができます。

ポイントの入力

ポイントを入力するには、まずポイント切り替えに使用する回路を決めます。これには、ツールバーの右側にあるポイントパレットを使用します。選択ツールをクリックすると、ツールバーに「>」のスイッチが表示されますので、これをプレスして選択します。⁠A」が普通のレールで、⁠P」はポイントが分岐側に切り替わっている状態、⁠p」はポイントが直進側に切り替わっている状態です。今回は「01」「P」にしてみましょう。

  1. 選択ツールをクリックします。
    →ポイントパレットに「>」が表示されます。
  2. 「01」の右にある「>」をプレスします。
    ⁠APp」のドロップダウンボックスが表示されます。
  3. 「P」のところまでドラッグして、リリースします。
    ⁠P」が選択され、⁠01」が黄色になります。

レールツールをクリックすると、さきほどとは異なり、給電パレットの01は「+」となり、ポイントパレットの01が「P」になります。この回路はポイント切り替え用なので、レールへの給電に使用することはできません。そのかわり、右側のポイントパレットをクリックしてポイントとして利用することができます。

では、以下のように操作してみてください。

  1. 給電パレットの「02」をクリックします。
    ⁠02」が選択されます。
  2. 「01」の右側の「P」をクリックします。
    ⁠01」の右側の「P」が選択され、同時に「R」の選択が解除されます。
  3. レイアウトエリアでドラッグを行います。
    →レールが入力されます。
  4. 「01」「+」のところをクリックします。
    ⁠P」「p」になり、同時に今入力したレールの表示が細線に変わります。
  5. 入力エリアで、今入力した線と「く」の字の形になるように、新しいレールをドラッグ入力します。
    →レールが表示されます。
  6. 「01」「+」をクリックします。
    ⁠P」「p」が切り替わり、レールの表示も切り替わります。

ポイントでない普通のレールは「R」で入力します。また、いったんポイントとして入力した場合でも、前述の方法で選択し、⁠R」をクリックすることで、普通のレールに変更することができます。

Nゲージのポイントは、ポイントが切り替わると同時にレールの配線も切り替わるようになっています。このため、共通側の1ヵ所に給電する場合と、分岐先に合計2ヵ所給電する場合とで入力方法が変わります。今回は両方とも「02」から給電するようにしましたが、それぞれを別の回路につなぐこともできます。

シミュレータで確認

残りの半円も同様に入力してください。それが終わったら、最後に車両を配置します。

右下のレールを選択してから、ツールバーの上部にある「T」のスイッチをクリックしてインディケータを点けると、レールの中央に車両が表示されます。もう一度クリックすると車両が削除されます。なお、ポイントの上には車両を置けませんので、もし「T」のスイッチが灰色でクリックできない場合は、⁠R」のスイッチをクリックして普通のレールに変更してください。

さて、ここで入力したレイアウトは、シミュレータで動かすことができます。レイアウトエディタとシミュレータが同じrailconf.txtを共有している場合は、レイアウトエディタ側で給電パターンやポイントを操作すると、その場でシミュレータ側に反映されます。この場合、シミュレータによって動いた車両は、自動では元の位置には戻せませんので、ファイルのコピーを取っておくか、位置をおぼえておいてください。

レイアウトエディタとシミュレータが別のディレクトリにある場合などは、railconf.txtをシミュレータ側にコピーしてから、シミュレータのURLを開きます。

制御プログラム(今回の場合はrail12.php)を実行すると、シミュレータ上で自動走行できます。エラーが発生すると、車両が途中で左上隅に移動してしまいます。このときは、レールがどうしがきちんとつながっているか、ポイントに車両がある状態でポイントを切り替えていないかなどを確認してみてください。

レイアウトの作成~シミュレータによる確認
ニコニコ動画:https://www.nicovideo.jp/watch/sm8029137
実際より50%程度速めたものを掲載しています。

開発の舞台裏

さて、このレイアウトエディタですが、どういう過程で作ったのかを少し書いてみたいと思います。

実は筆者は、Webベースの図形エディタを作りたいとずっと思っていました。TRONのGUI-OSであるBTRONをずっと使っていたので、この連載の図などもBTRONの図形エディタで描いて、編集部で清書していただいています。残念ながら、今はプログラミングが中心なので、できればLinux上だけで完結させたいと考えていました。以前SVGを扱ったときに、JavaScript+SVGによる実現可能性までは確認していたのですが、そこからは進んでいませんでした。

今回のレイアウトエディタは、この図形エディタの検討がベースになっています。図形エディタと違うのは、パーツがレールしかなく、色のかわりにつなぐ回路やポイントを指定するといったところです。

では、どのように考えて設計を行ったかを見てみましょう。

意思決定ポイント

まず、Webベースのエディタアプリケーションで問題になるのは、複数のユーザからアクセスされるということです。これは、編集対象のデータをどのように保持するかという設計に影響します。

もう1つ問題になるのは、サーバ側とクライアント側の分散環境になるということです。これは、どの情報レイヤでサーバとクライアントを切り分けるのかという設計に影響します。

それ以外にもユーザインターフェースやデータモデルといった要素がありますが、これはWebに限った話ではありません。今回の場合、ユーザインターフェースはBTRONの図形エディタをベースにしています。また、データモデルはシミュレータの設定ファイルをそのまま利用しました。

ユーザインターフェースの検討メモ
ユーザインターフェースの検討メモ

これを図にすると、以下のようになります。

図4 設計レイヤ
図4 設計レイヤ

サーバとクライアントの切り分け

ではここで、レールをクリックして選択するという処理を考えてみてください。どのような方法が思いつくでしょうか。

  1. サーバサイドイメージマップ
    単に座標をサーバ側に渡し、サーバ側で座標判定をおこなって、レールが選択されたページを返してもらう
  2. クライアントサイドイメージマップ
    クリックされたレールのIDをサーバに渡し、サーバ側からレールが選択されたページを返してもらう
  3. JavaScript
    クリックされたレールを、ブラウザのJavaScriptで処理して選択表示する

最初の方法は、サーバとブラウザの通信の抽象度を「画像」としています。画像の中身についてはブラウザ側はまったく知らず、渡された画像を表示し、クリックされた座標を返すということです。これに対して、2番目と3番目はIDレベルの抽象度になっています。この場合、各レールの座標がサーバから渡されているわけですから、レールも画像ではなく、SVGの図形としてブラウザ側で描画するのが自然でしょう。

さらに3番目では、選択状態の管理もブラウザ側でおこなっています。この場合、選択だけでは通信は発生せず、移動や変形などがあってはじめてサーバと通信するという形になります。サーバの仕事の大部分はデータモデル(ファイル)との入出力となり、データベースサーバに近いスタイルとなります。

なお、今回のシステムでは1番目の方法を採用しています。具体的には、ラバーバンドの描画のみをSVGで行い、結果はフォームに入れてサーバに送って、変更後のページを受け取っています。これは、前回作成したシミュレータのソースを流用したというのが大きいのですが、欠点としてサーバが画像を生成するため負荷が大きくなる点があります。また、JavaScriptを含んだHTMLをまず送信し、その中のIMGタグによって画像のリクエストが発生するため、2つのリクエストの間で情報を共有する手間もかかります。

図3 ドラッグ入力時の通信
図3 ドラッグ入力時の通信

Ajaxはページ遷移を伴わない通信という意味を含んでいますが、今回のシステムでは、マウス操作のたびにフォームをPOSTしています。これによりサーバとブラウザとで内部状態が食い違うのを避けられるため設計が簡単になるのですが、マウス操作のたびにスクロール位置が戻ってしまうという欠点もあります。

余談ですが、JavaScrpitやSVG、あるいはクライアントサイドイメージマップをサポートしないブラウザが相手の場合は、このサーバ側での画像生成が唯一の手段となります。この場合、最初のクリックで始点を描画した画像を返し、2回目のクリックで入力とするユーザインターフェースが考えられます。ただし、今回のシステムではここまで実現してはいません。

複数ユーザからのアクセス

さて、Webベースのアプリケーションには、複数のユーザからアクセスされるという問題もあります。また1人であっても、ブラウザで複数のウィンドウを開いていたり、⁠戻る」操作を行ったりすることがあるので、その辺まで含めた対応が必要です。

たとえば、選択している給電パレットの番号を、どこに保持するかを考えてみましょう。

  • フォームに持たせる
  • cookieに持たせる
  • JavaScript変数に持たせる
  • サーバ側のユーザ情報に持たせる
  • サーバ側で共有する

フォームに持たせる方法は、各ウィンドウで別々の設定が可能です。そのかわり、⁠戻る」操作によって設定も戻ってしまいます。cookieは「戻る」の影響を受けないかわりに、ブラウザ内で共有されてしまいます。ログインのテストをしているとき、別ウィンドウで管理者でログインすると、その直後から全部のウィンドウで管理者権限でアクセスされてしまったりしますが、これはユーザ情報をcookieに持たせていることによるものです。

JavaScript変数に持たせる方法は、Ajaxのようにページ遷移を伴わないシステムでは有効です。ただし「戻る」操作などを行ってページ遷移が発生してしまうと、JavaScriptが中断して変数の値が失われてしまいます。これは、Google Mapsで目的の場所まで拡大したあとに、誤って「戻る」操作をすると発生しますね。

サーバ側のユーザ情報に持たせる方法は、ユーザがログインするという前提があります。この場合、あるユーザが変更をおこなっても、他のユーザには影響を与えないといった、高度な処理が実現できます。ただ、この方法ではログインしないと使えないといった問題があり、管理も複雑になります。また、ログイン情報をフォームに持たせるかcookieに持たせるかという選択があり、上記の問題につながります。

サーバ側で共有というのは、今回のシステムのrailconf.txtのようなもので、誰かが更新すればただちに全員に影響を及ぼすというものです。

今回のシステムでは、選択している給電パレットなどの情報は、ブラウザのcookieに持たせています。これは前述の、HTMLと画像が別々のリクエストで来るという問題に対応しやすいという点によるものです。

また、給電パレットの情報だけでなく、データ本体の共有をどうするかという問題もあります。Wikiなどでは、プレビュー段階まではフォームに持たせ、最後に更新を反映するスタイルになっています。大きい変更の場合はブラウザで操作するのではなく、ローカルのテキストエディタで編集したものを貼り付ければよいので、これでも大きな問題にはなりません。図形エディタの場合、ローカルのテキストエディタで編集するということができないので、理想的にはユーザごとの履歴管理がほしいところです。今回はとてもここまではできないので、シングルユーザ用と割りきって設計しました。

コーディング

実際のプログラミングですが、最初はシミュレータの画像表示をそのまま使い、レール入力を実装しました。レール入力は、結果が画像に反映されるので、入力途中のラバーバンド描画だけを作成すれば動かすことができます。

次に、ツールバーを作り、選択処理に進んでいきました。選択処理は、最初はドラッグで囲うと1つだけ選択されるという単純なもので、あとからクリック選択や複数選択に拡張しています。複数選択の表示方法やドラッグ検出の方法はずいぶん迷ったのですが、今回はとりあえずスクリプトを動的に生成して埋め込む形にしました。この辺はいろいろ改良したいところです。

なお、今回のシステムを作る上で、以下のソフトウェアに大変助けられました。紙面をお借りしてお礼を申し上げます。

Firebug
Firefox用の開発者向けアドオンです。個々の通信の詳細を見たり、DOMレベルで変数を調べたりできます。
xdebug
PHP用のエクステンションで、エラー表示を詳細化してくれます。
xcache
PHP用のエクステンションで、coveragerという機能を利用すると、カバレッジテストができます。残念ながら、coveragerはWindowsでは動かすことができませんでした。
そのほか、LinuxやApache、PHP、Firefoxなどの、土台となるオープンソースのソフトウェアにはいつもお世話になっております。

本連載に掲載したプログラムは、今回のレイアウトエディタや前回のシミュレータも含め、すべて自由に利用していただいて構いません。ただ、コードとしては読みにくいところがありますので、その点はご容赦いただきたく思います。読者のみなさんも、おもしろいアイディアを思いついたら、ぜひいろいろ試してみてください。

今週発売の「組込みプレス」Vol.16に、この特集で紹介したNゲージシステムの、組込み開発側からのアプローチについてまとめました。コントローラの回路図なども紹介しています。合わせてお読みください。

おすすめ記事

記事・ニュース一覧