アイドルを捜せ

このところ少しハードな内容が続いたので、今回はアクセルをゆるめてアイドル運転をすることにしよう。取り上げるテーマも、時間を持て余しているCPUの、アイドルタイムの利用法だ。

オフィスにはアイドルがいっぱい

オフィスに着いてから帰るまで、たとえば9時間コンピュータの電源をオンにしているとして、その間ユーザーが画面に向かって作業をしている時間はどれくらいあるだろうか。しょっちゅう使っているようでも、実際にキーボードにさわっている時間は通算してせいぜい数十分程度だろう。データインプットの専門家など特別な人は別として、数時間に上るというのは余り考えられない。しかも、ワープロなどのアプリケーションの場合、コンピュータから見るとほとんどの時間はインプットの待ち状態。オフィスで最も活躍するアプリケーションがスクリーンセイバーというのはブラックユーモアだが、多くの場合、真実である。

この待ち時間を利用して何か有効な作業を行わせることができないか。今回は、アイドルタイムの活用方法を検討してみる。

idleメッセージ

idleチェックの流れ図 ハイパーカードは全てメッセージによって働いている。ボタンをクリックするとmouseUpというメッセージが送られるし、メニューを選ぶとdoMenuというメッセージが発行されて命令が実行される。ユーザーはメッセージボックスから命令を打ち込むことで、直接メッセージを送ることも可能だ。そして、こうしたメッセージが何もない待機状態の間、ハイパーカードでは、常にidleというメッセージがカードに対して送られている。(図1)

「メッセージウォッチャ」を開いてみると(*注1)、画面1のように連続したidleメッセージが確認できるはずだ。このidleの発行は、ユーザが指示をしなくても、ハイパーカードが勝手に行ってくれる作業である。従って、このメッセージをうまく使うと、コンピュータの空き時間に仕事を自動的に実行させることが可能になる。

idleによる自動操作(復習)

8月号で検討した「会議室予約システム」では、このidleを利用したスタックの強制解放の仕組みを組み込んでいた。一定時間入力がなかったら自動的にスタックを閉じる命令を発することで、共有環境での資源の有効な利用をはかるというものだった。この仕組みを少し復習してみよう(リスト1)。

まず、スタックを開いた時点でタイムリミットの時刻をグローバル変数に保存する(4行目)。そして、バックグラウンドにおいたon idleハンドラで、現在時刻がタイムリミットを越えていないかを監視する(9行目)。もし越えていたら、その時点でスタックを閉じる命令を発してスタックを解放する。

ここでのポイントは、命令の実施時刻、つまりタイムリミットをグローバル変数に設定するというところだ。単に空き時間を利用するというのではなく、コンピュータの内蔵時計をチェックすることで、特定の仕事を特定のタイミングで実施させる。この時刻監視の構造をうまく設計できれば、自動操作システムは半分できたも同然なのだ。

以上をまとめると、idleによる自動操作の公式は

  1. 命令を実施する時刻を設定(グローバル変数)し
  2. idleメッセージのたびに現在時刻をチェックし
  3. 指定時刻になったら命令を実行する

ということになる。

定時作業の自動化

定期的に行う作業なら、グローバル変数を使わず、指定日時をスクリプトに直接書き込んでも良い。このとき注意しなければならないのは、時刻の書式である。マッキントッシュは時刻を12時制、24時制のどちらにもできるので、

if the time is "15:00" then

のようなチェックでは正しく働かない可能性が残る(*注2)。これをクリアするには、convert命令で時刻をいったんdateItems形式にすればよい。例えば

put "95.09.18" into theDate
convert theDate to dateItems

とすると、変数theDateは"1995,9,18,0,0,0,2"という形になる。これは順番に年,月,日,時,分,秒,曜日というアイテムに日付が変換されたもので、時刻が常に24時制で比較できる上に、曜日も調べられるというおまけが付く。

これを利用すると、例えば「毎週月曜日の午前中にサーバーに報告書ファイルを提出する」というルールが守られているか、自動チェックを行うことができる(リスト2)。5行目で現在時刻(the time)を変換し、6行目で曜日(item 7)、時(item 4)、分(item 5)を調べる。月曜日の午後1時になったら7行目で指定ファイルが提出されているかどうかを確認するのだ。

なお、2〜4行目で行なっているのは、同じ警告を必要以上に繰り返さないための手続きである。idleメッセージは1秒に何十回も発行されているから、「午後1時0分に実行する」と指定された命令は、1時1分になるまでの間、延々と繰り返されてしまう。そこで、命令を処理するたびに現在時刻(何時何分)をグローバル変数lastCheckTimeにセットしておき(4行目)、次にidleが巡ってきたときに変数と現在時刻を比較して、同じ時刻なら処理をキャンセルするのである。こうすることで、このチェック命令は1分間に1回だけ実行されるようになるわけだ。

繰り返すが、自動操作は実行タイミングの設定がキーポイントである。サンプルとして、指定時刻や指定秒後に命令を実行するidleハンドラを作成するスタック(画面2)を用意したので、実際にその働きを確認してみて欲しい。

ハイパーカードを遠隔操作

idleを使うと、自動作業だけでなく、別のコンピュータを遠隔操作するというアクロバティックな仕掛けを作ることができる。

この手法は、原理的には前節で述べた定時自動操作と違いはない。常にidleメッセージを監視し、ある条件が満たされたら命令を実行する。異なるのは、命令とそれを実施するタイミングをあらかじめ決めておかず、外部から命令内容とその実行指示を送るという点だ。

実行時に命令文を変数として与える方法は、8月号のネットワーク活用の記事の中でも紹介した。doを使って外部ファイルに保存した文字列を実行するという手法である。すなわち、ファイルを読み出す関数をReadFile()とすれば

do ReadFile(<command file>)

という形になる。命令はスクリプトに書き込まず、外部ファイル<command file>にテキストとして用意しておく。

命令を与える方法が決まったら、次の問題は実施のタイミングである。外部ファイルに実行内容があるのだから、そのファイルの更新時刻を基準にタイミングを判断するのが分かり易い。ここではidleのたびに<command file>の更新時刻を調べ、ファイルが新しくなっていたら、そのファイルを読み出してdoで実行することにしよう。指示を与える側から言えば、ファイルを更新することで遠隔コンピュータに作業を実行させられるということだ(図2)。

残念ながらハイパートークにはファイルの更新時刻を調べる関数は用意されていないので、XFCNを使う。スタックHyperLauncherに用意したFileInfo()は、引数にファイル名を与えると、4番目のアイテムとしてファイルの更新時刻をsecondsフォーマットで返す。これを使って、前回の命令を実行した時刻と比較をするのである。

遠隔操作スクリプトの実際

以上をまとめたのがリスト3である。2〜4行は、idleの処理を頻繁に行いすぎないようにする前処理のバリエーション。この例では、一度処理を行ったら60秒間はidleが送られてもパスするという設定になっている(リスト2と比較してみよう)。

5行目以降が遠隔操作の部分である。命令ファイルの更新時刻を調べ、前回行った命令の時刻(「LastCommand」というフィールドに保存してある)と比較する。ファイルが変更されていなければ、その命令は既に実行済みということになるので、何もせずに通り過ぎる。もしファイルが新しくなっていたら、新たな指示が送られたということだ。まずこの時刻をフィールド「LastCommand」に記録し(つまり実施済みであることを記録する)、そのうえでファイルをReadFile()で読み出してdo命令で実行する。

ごく短い簡単なスクリプトだが、これでもちゃんと遠隔操作はできる。命令を保存ファイルを更新して保存するだけで、全てのコンピュータに1分以内に指示がきちんと届く。何台ものコンピュータが次々にこの指示に反応していくのを見るのは、なかなか面白いものだ。

電子速達メール

最後の応用として、メッセージの即時送信を考えておこう。ここまできたらあとは簡単。前項の遠隔操作のスクリプトを、命令ファイルを読み出す代わりにメッセージファイルを調べて表示するように変更すると、たちどころに速達メールができあがる(画面3)。リスト4はほとんどリスト3と同じ構造である。フィールドに記録した時刻よりメッセージファイルが新しければ、メッセージ表示用のカードに移動してファイルの内容をテキストフィールドに書き込む。最後に

open long name of HyperCard

としているのは、この命令でハイパーカードをフォアグラウンドに呼び出し、ほかの作業をしているときでも強制的にメッセージを読んでもらおうという戦略である(漢字Talk7以降のみ有効)。

6、7月号でスタック起動時にメッセージを表示する方法を紹介したが(1995/6号:電子掲示板及び1995/7号:電子掲示板との連動参照)、場合によってはすぐにメッセージを知らせたいときもあるだろう。両者を組み合わせて使うと効果的な情報伝達ができると思う。

ハイパーカードのスクリプトは別のアプリケーションのバックグラウンドでも働くので、これらの機能を実現するにはハイパーカードが起動していさえすればよい。逆に言うと、ハイパーカードが常に立ち上がっていなければ、当然のことながらこの便利な自動作業や遠隔操作は使えないことになる。このためだけでも、6月号で述べたようなローンチャをハイパーカードで作成し、常に起動しておく意義は十分にあると思う。ハイパーカードはいつでも使えるように、起動項目に入れておこう。これが快適なハイパーカード・オフィスの基本である。

*注1

メッセージボックスから"mw"という命令を実行して開くことができる

*注2

ハイパートークでは時刻も文字列として扱われるので、15:00と3:00 PMは異なるものとなってしまう。

リスト

リスト1

 1: on openStack
 2:   global TimeOut
 3:   ...
 4:   put the seconds + 300 into TimeOut -- 5分間をタイムリミットとする場合
 5:   ...
 6: end openStack

-- idleメッセージを監視する。バックグラウンドに置く。
 7: on idle
 8:   global TimeOut
 9:   if the seconds > TimeOut then
10:     go stack "HyperLauncher" -- pop cdなどでもよい。スタックを強制的に閉じる。
11:   else
12:     pass idle
13:   end if
14: end idle

リスト2

 1: on idle
 2:   global lastCheckTime
 3:   if the time is NOT lastCheckTime then
 4:     put the time into lastCheckTime
 5:     convert the time to dateItems --この場合は結果が変数itに納められる
 6:     if item 7 of it is 2 AND item 4 of it is 13 AND item 5 of it is 0 then
 7:       if there is NOT a file <報告書のファイル名>
 8:       then answer "報告書が提出されていません!"
 9:     end if
10:   end if
11:   ...
12:   pass idle --passの解説参照
13: end idle

リスト3

 -- fld "LastCommand"は「テキストを共有」に設定しておく
 1: on idle
 2:   global nextAccess
 3:   if the seconds < nextAccess then pass idle
 4:   put the seconds + 60 into nextAccess
 5:   get FileInfo(<command file>)
 6:   if item 4 of it > fld "LastCommand" then 
 7:     put item 4 of it into fld "LastCommand"
 8:     do ReadFile(<command file>)
 9:   end if
10:   ...
11:   pass idle
12: end idle

リスト4

 -- fld "LastMessage"は「テキストを共有」に設定しておく
 1: on idle
 2:   global nextAccess
 3:   if the seconds < nextAccess then pass idle
 4:   put the seconds + 60 into nextAccess
 5:   get FileInfo(<message file>)
 6:   if item 4 of it > fld "LastMessage" then
 7:     put item 4 of it into fld "LastMessage"
 8:     go cd "MessageBoard"
 9:     put ReadFile(<message file>) into fld "MessageText"
10:     open long name of HyperCard
11:   end if
12:   ...
13:   pass idle
14: end idle

用語

convert

本文でも説明したとおり、時刻の表現形式を変換する命令。変換できるフォーマットはdateItemsのほかにseconds, short date, long date, short timeなどがあり、場合によって使い分けることができる。特に、時刻の比較をするとき、ハイパーカードでは文字列形式(the date, the timeなど)のまま前後を比べることはできないが、いったんsecondsフォーマットにするとこれが可能になり、大変便利である。今回の遠隔操作でも、ファイルの更新時刻をseconds形式で取得することで、ファイルが新しいかどうかの比較を行っている。

pass

サンプルリストで全て最後にpass idleという1行が加えられているのに気付かれただろうか。ハイパーカードはカード、バックグラウンド、スタックといった階層構造になっていて、メッセージはこの順序に伝えられて行き、ハンドラが見つかったところで処理される。カードにon idleハンドラがあればidleメッセージはここで処理され、バックグラウンドには届かない。このとき、pass idleとしておくと、このメッセージが更に次の階層、この場合はバックグラウンドにも送られる。通常の処理をバックグラウンドで行っており、カードで特別な処理を行った上で通常の処理もこなしたいときは、このようにpass idleを置いておく必要がある。

ハンドラの途中にこのpassを置くと、メッセージは直ちに次の階層に送られ、残りのハンドラは処理されない。リスト3の3行目で、次の命令実施時刻になっていないときにpass idleとしているのはこのためである。

一般に、openCard, openStackなどの汎用ハンドラは最後にpassしておくことが多い。

テキストを共有

バックグラウンドフィールドの属性の一つ。通常バックグラウンドフィールドに記録する内容はカードごとに異なるが、これがチェックされていると、同じ内容が全てのカードで共有される。リスト3,4でファイルの更新時刻をどのカードからでも確認できるようにするためには、前回のファイル更新時刻を「テキストを共有」しているバックグラウンドフィールドに保存しておかなければならない。

(MacUser Japan, October 1995)