2011年6月23日木曜日

Inno Setupにハマる

比較的柔軟な動作設定・定義ができるという点で使っているInno Setupだけれども、アンインストール動作で不穏な動作に遭遇した。結論から言えば、Inno Setup自体は問題なかった(仕様どおりの動作)のだけれども、あまり意識していないとハマる可能性があるので備忘録として残しておくことにした。

というのは、公開しているアプリのバージョンアップなどで、古いバージョンをインストールしたまま同じフォルダに上書きインストールしたときはアンインストール動作にも気を配ろう。
具体的に言うと、古いバージョンのときに指定した[UninstallRun]セクションの内容は、上書きインストールした新しいバージョンの[UninstallRun]セクションとともに、アンインストール情報ファイル"unins???.dat"に両方記録されている、という点である。つまり、上書きインストールを行ったアプリのアンインストール動作としては、

  1. 新バージョンで指定した[UninstallRun]セクション
  2. 旧バージョンで指定した[UninstallRun]セクション

の順で実行されるので、新しいバージョンでインストーラ/アンインストーラ関係のファイルを更新したり、動作を変更したりすると、上の2の動作のときにおかしくなる可能性がある。

これを回避する方法としては、


  1. 新旧バージョンでインストーラ/アンインストーラの動作を変更しないこと
  2. アンインストール動作を変更したとしても、旧形式でのアンインストール動作で不具合を起こさないように注意すること
  3. 新バージョンをインストールする際に、旧バージョンをアンインストールしてから新バージョンをインストールするようにユーザに喚起するか、新インストール側でそういう対策を行うこと
という方法が考えられる。

2011年6月17日金曜日

ドアミラーって・・・

車を普通に運転してたら見ますよね?>ドアミラー

  • いや「私の車はフェンダーミラーだから見ない!」という天の邪鬼な返答は置いといて・・・


今日の通勤途中、ドアミラーをたたんだまま走る車を見かけました。しかも高速道路でです!結構飛ばしていましたし(120km/hくらい出てたかも)、いくら交通量が少ないとはいえ車線変更もバンバンやってました。いったい、どうやって安全確認をしているんだろうか?

ルームミラーである程度後方は確認できるけど左右の斜め後ろには明らかに死角があるし、ドアミラーを使ってもかなり死角は広いと思う。仮に、車が来ていないことをずいぶん前から長い時間ルームミラーを使って後方を確認していたとしても、たま~に恐ろしいくらいのスピードであっという間に近づいてくるベンツやセルシオがいることを考えると、その方法も決して安心はできない。

ハッキリ言って、こんな人には車の運転をしてもらいたくない。ちょっと怒り心頭です!
ここでナンバーをババーンと晒してやろうかとも思うけどさすがにちょっと問題がありそうなので一部伏せ字で・・・

横浜 501 ま 5○○1 の 白いシルビア

この車を運転している人!ちゃんとドアミラーも(もちろん目視も)使って左右斜め後ろの安全確認をしてください。

2011年6月10日金曜日

TOPPERSを触ってみる

以前から気になっているTOPPERSプロジェクト。以前にも少し触ってみたことはあるのだけれど、コンフィグレータがどうとか、静的APIがどうとか、初めて触るにはちょっとややこしいor面倒な部分が多くて挫折していた。う~ん、きっとコンフィグレータは後々良さが分かってくるんだろうけど、これまでになかった新しい何かを身につける・・・いや少なくとも乗り越えるにはちょっとしたパワーが要るんだなぁ。その点、FreeRTOSはカーネルソースコードも含めてコンパイル&リンクするだけでOSに必要な機能が使えたから簡単に取り組めたんだけどなぁ。ま、新しいものを素直に受け止められないのはオジサンになった証拠か。

でも今回はごにょごにょ言わずに何とか先に進めてみた。
ターゲットはcortex-m3(stellaris)だ。幸いcortex-m3向けの機種依存部分を提供してくださっている方がいたのでありがたくダウンロードさせていただいて、当面の評価を行おうと思う。

まず、TOPPER/ASPの最新版をダウンロードして展開。更に、cortex-m3依存部分も同じディレクトリに展開。doc/user.txtによると、やはりコンフィグレータcfgが必要らしい。が、ASPのフルパッケージに何故か入っていない。仕方ないので、またHPから探すと、今回はcfgのwindows向けバイナリが置いてあった。ソースも置いてあったけれど、目的はstellarisでtoppersを動かすことであって、コンフィグレータをビルドする方法とかはどうでもいい。で、ダウンロードしたcfg.exeはどこに置けばいいんだ?
とりあえず先に進むことにした。なるほど、他のオープンソース系と同じようにターゲットにあわせたconfigureスクリプトとmakeを行うのか。そして、configureスクリプトの実行はperlだと。そうか、shスクリプトはlinux系ユーザにはおなじみだけど、windowsユーザを考慮してまだ導入しやすいActivePerlなんかで対応できるようにしてるんだな、と勝手な推測。とりあえず、ActivePerlの最新版をダウンロードしてきてインストール、パスを通した後、コマンドプロンプトからperl ..\configure -T lm3s8962_gcc[ENTER]とタイプイン!何っ?エラー?PWDコマンドがないって?そりゃ、Windowsのcmd.exeにはPWDはありませんよ。ということは、ActivePerlはダメってこと?
う~ん、windows<オンリー>ユーザのためにperlを使うわけではないようだ。仕方ないのでActivePerlはソッコー消して、cygwinのbash上からperl/configureスクリプトを実行して事なきを得た。しかし、結局bash系の環境は必要なら何であえてperlを導入したのか分からない。shスクリプトのままじゃダメなの?perlは文字列処理が得意だから?

まぁ、いいや。とりあえずconfigureスクリプトは成功してmakefileができたようだ。そのままbashからおもむろにmake[ENTER]をバシッと。

またエラーですか。

今度はコンフィグレータcfgが無いと言っているようだ。cfgのあるべきパスは、../cfg/cfg/cfgですか。何ですか、そのパスは?まあよい、今回は素直にそのパスにダウンロードしたビルド済みcfg.exeを置いてみる。そして、再びmake[ENTER]をバシッと。

またまたエラーですか。

今度はTSKCTBXという識別子(たぶん構造体定義)が無いとおっしゃる。ハイハイ、どこか探してきますよ。というわけでソースツリーをgrep。しかし、TSKCTBXを定義している箇所が無い。いや、arch/m68k_gccの中に1つだけ発見した。でもなんかおかしいなぁ。名前から想像するに、TSKCTBXはタスク制御ブロックのようだけれど、その定義を発見したのはarch/以下。つまり、機種依存コードの中。こういう場合、タスク制御ブロックの定義って、機種依存ではなく、カーネル本体に含まれるべきなんでは?

困りました。TSKCTBX構造体を自力で定義せにゃならんのですか?と思っていたところ、偶然発見した。「CTBXはTSKCTBXに名前が変更されました。」

なんじゃそりゃ?

でCTBXで検索してみるとありました!やはり、機種依存部分のarch/arm_m_gcc/prc_config.hにありました。勝手にTSKCTBXに書き換えて、今度こそmake[ENTER]でサクッとコンパイル!

出ました。またエラーです。

今度は、hw_ints.hが見つからないと。この名前は見覚えがある。StellarisWareに含まれているヘッダファイルのようだ。includeサーチパスを見ると"LuminaryMicro_Driver"とか含まれてる。LuminaryはTIに買収されちまったよぉ~、StellarisWareに変わってパスも変更されたんだよぉ~。というわけで、makefileからインクルードパスの設定を変更して、などなど・・・。

どうやらダウンロードしたcortex-m3向け機種依存コードは、かなり古いもののようだ。実際、最終更新が2008年となっている。カーネル部分の更新について行けてないのか・・・。あきらめ半分でmake[ENTER]ポチっと。

お約束のエラーです。

今度は、INHINIB構造体にint_entryというメンバは存在しない、EXCINIB構造体にexc_entryというメンバは存在しない、という2つのエラー。INHINIB/EXCINIB構造体の定義を探してみると、いやint_entryとexc_entryはちゃんとFP型で宣言してある。FP型も定義してある。もしかしてこの部分はコンパイル時に読み込まれない(#if~#endifではじかれている?)可能性もあると思って、#errorとか適当なものを埋め込んでみるとちゃんとコンパイル時に#errorが報告されるのでこの定義を通っているようだ。では何故?・・・

分かりました。機種依存コードの中でint_entryとexc_entryを#defineで_kernel_int_entry/_kernel_exc_entryにリネームしてある。そうだ!思い出したゾ。

以前TOPPERSに取り組んでみてイヤになったのは、この#defineによるリネームのせいだ。コイツのせいで今回のようなコンパイルエラーが出て困ったんだった。こういうリネームをやられると、どの名前が本当の名前で、どこで定義されているのが実際に使われる名前なのかがまったく分からなくなる。しかも、int_entryのような単純な名前を入れ替えられると、今回のようにたまたま構造体のなかで使ったメンバに同名のものがあると、それもリネームされたり、されなかったりで、何が本当なのか訳が分からない。
どうしてこういう実装にしたのだろうか?結果的にこれがもっともスマートな解決方法なのかもしれないけど、私のように事情をよく知らない者がメンテなりポーティングする段になると、非常にメンテナンス性が悪くなると思うし、思わぬ不具合をはらみかねないと思うのだが。少なくとも#defineで名前の置き換えがされることを前提にしているシンボルなり関数があるのなら、その名前は置き換えられることがもっと区別しやすいような名前にすべきだと思う。今回のようにint_entryのようなありがちな名前だと、グローバル関数としてのvoid int_entry(void)をリネームしたいのか、INHINIB構造体中のint_entryメンバをリネームしたいのか判断できない。

で、結局この部分をどう解決すべきか?本当に手がかかるなぁ、ブツブツ・・・


[2011/6/10 18:45加筆]

その後の調査で分かったこと。

○#defineによる関数名のリネームは、ユーザが書くアプリケーション側の関数名との競合を避けるためらしい・・・
>>それなら敢えてリネームしなくても、ユーザー側が競合を回避すればいいだけではないか?
>>または、ユーザー側で使いそうな名前の関数をTOPPERS側で使わない方がよいのでは?実際、doc/user.txtにはtoppers_接頭辞を予約していると書いてあるんだから、一目でTOPPERSの「内部関数だな!」と分かる名前をつけることは、ソースコードの可読性を挙げるためにもむしろ有効な手法ではないか?
>>今回の場合は、Stellaris用の機種依存コード側でint_entryという名前をつけたこと自体が失敗だったのではないかと思われる(成果をダウンロードさせてもらっておきながら、なんとも勝手な言い分だろう>私)

で、とりあえず上に書いたint_entryとexc_entryの妙なリネームを回避するとサンプルをビルドすることができた!

2011年5月31日火曜日

イライラするなぁ

どうもStellarisを使っているとイライラする。

○データシートに書いてある内容が不十分
○データシートに書いてある内容どおりに動かない
○StellarisWareは間違いだらけ

確かに、アプリケーションノートやサンプルあること”だけ”をやっている限りはちゃんと動くし、パフォーマンスもそこそこよいと思う。もちろん、CPU以外の機能についても豊富で充実はしている。

しかし、そのCPU以外の周辺機能は、本来想定された正常な動作から外れるともうガタガタだと言わざるを得ない。エラー時の対応・回復処理とか、処理途中でキャンセルするとか、そういうことをやろうとするととたんに不可解な動作をし始める。

例えば、uDMAのキャンセル方法。
データシートによると、DMAENCLRの該当ビットに1を書き込めばDMA動作を停止するとあるが、実際にはちゃんと止まってくれない。いや、確かに“止まっている”のだけれど、uDMAコントローラ内部では転送途中の状態をまだ記憶しているようで、新しい別の転送先/転送元アドレスを指定してDMAENSETのビットをセットしても、新しいアドレスには転送してくれない。
結局、DMAを停止させるために、まずDMAの要求トリガを発生させる機能(UARTやUSBなど)でDMA要求を出させないようにした上で、そのDMAチャンネルにソフトウェアトリガを繰り返し発行してDMA転送を“本当に”完了させる、という方法を採用するしかなかった。

他にも、USBの転送のキャンセル方法についても不具合発見。
USBデバイスとして機能させ、USBデバイス→ホスト方向への転送を途中で止めたくなったとする。つまり、ホストへ転送するためにFIFOに入れておいたパケットの内容を取り消して、ホストからIN-NAKとさせたい訳だけれど、データシートどおりにUSBTXCSRLnのFLUSHに1を書いてみてもFIFOの内容は消えていない(ホストからINトークンでパケットが取れる)し、TXRDYビットがクリアされると書いてあるけれど実際には自分で0を書いて消さなければ消えない。どうやら、FLUSHビットへに書き込みは、FIFO内部のポインタをリセットするだけでFIFOの内容を消したり、USBコントローラに送信可能(TXRDY)であることをキャンセルさせたりはしていないようだ。
しかも、FLUSHでFIFOをクリアすると(正確にはクリアするためにTXRDYに0を書き込むと)割り込みが発生する。その割り込みは要らないだろう。しかも、このデバイスは割り込みを発生させた細かい要因を教えてくれないので、「FIFOクリア直前にホストがINトークンでパケットを転送した」のか、「FLUSHによるキャンセルが要因」なのか区別がつかないじゃないか!バカヤロウ!

全体的に見て、正常な場合の動作しか想定しておらず、「もしもこういうことが起きたら・・・」という異常系での振る舞いについてはほとんど考えられていないのではないだろうか?あるいは、文化的な違いから、青い目の人は「異常が起きたら全部リセットして最初からやり直し、それでいいじゃん?」的な考えなんだろうか。

とまぁ、悪態をついてみたところで、巨大企業であるTIが動いてくれるわけでもなく、せいぜいこんないところを愚痴を書き連ねるのが関の山って所か・・・、orz

2011年5月6日金曜日

Stellarisのバグ?(その2)

UART+uDMAを使った受信において気づいたこと。

例として、10バイトのデータをuDMAを使って受信することを考える。データシートやサンプルに従って設定すれば期待通りに動作した。しかし、不測のデータ受信やエラーリカバリ処理に難儀した。

通信相手が10バイトではなく13バイト送ってきたと仮定する。
uDMAをBASICモードで設定していれば、まず最初の10バイトをDMA受信した時点で割り込みが発生した。これは期待通り。uDMAをBASICモードで設定しているので、この時点でuDMAの動作はSTOPになっている。
問題は、このあと残りの3バイトを受信したときにStellarisがどのように振る舞うかだ。
このときStellarisは残り3バイトについて、通常の(DMAを使わない)ソフトウェア+割り込みを使う方法と同じように、受信割り込みを発生させてしまう。ここで誤ってその3バイトの受信データをソフトウェアで読み出してしまうと話がややこしくなる。

UART自体は、RXDMAEフラグがセットされている以上は受信データが1バイトでも検出されると、そのUART回路内部でDMAリクエストを保持してuDMAコントローラの方に送っているようで、たとえUARTの受信FIFOの内容をすべてソフトウェアで読み取った後でもそのDMAリクエストはUART内部で残り続けている。つまり、余分な3バイトをソフトウェアで読み取ってもうFIFOには受信データが残っていなくても、次回uDMAの受信設定を行うとまだ保持され続けているUARTからのDMAリクエストによって1バイト分の受信データ転送トランザクションが発生してしまう、という誤動作につながるようだ。

これを回避する方法としては、

(A)DMAを使うときはソフトウェアで直接FIFOを読み出すようなことはしないこと。

(B)上記のような3バイトを「余分なデータ」として読み捨てる場合は、ソフトウェアでFIFOを読み出しても良いが、次回DMAを設定する前にUARTからのDMAリクエストをクリアする方法を行うこと。

の2案が考えられる。
上記の例で余分に受信された3バイト分が、本来次に受信されるべき10バイトのうちの先頭3バイトであれば(=すなわち、正常な次のパケットの一部などであれば)、この(A)を採用してそのまま次のDMA受信を設定&開始すれば問題なく(DMAで)受信される。

しかし、受信された3バイトが本来必要ではないゴミデータであればそれを読み捨てる(B)案を採用せざるを得ない。その(B)案を行う具体的な方法としては、

(B-1)Software Reset Control 1レジスタのUARTnビットを使ってUARTそのものをリセットしてしまう方法

(B-2)UART ControlレジスタのUARTENフラグにいったん0を書いて、1に戻すという方法

の2つが有効であるようだ。
(B-1)の方法は、UARTの設定そのものがリセットされてしまうので、再設定を余儀なくされる。
(B-2)の方法は、UARTの設定は保持されるがUARTENに0を設定している瞬間は受信処理が止まるのでその瞬間に受信データが来ると受信し損ねてしまう、という問題が残る。
どちらにしてもUARTの動作は一瞬でも止まるので、データを受信し損ねる可能性は排除できない。

もうちょっとうまいエラーリカバリ方法を用意してくれないものかなぁ。

2011年5月2日月曜日

Visual Studio 2008の「へぇ~」な機能

Windows VISTAや7では、システム関係のファイルの書き換えに関してちょっとした保護機能が追加されている。WINDOWSフォルダやProgram Filesフォルダは、ファイルを変更しようとするといくらadmin権限でも確認ダイアログが出てくるし、アプリケーションでProgram Files以下に自分用の設定ファイルを保存するような(お行儀の悪い)アプリケーションでは、ファイルの保存先をコッソリとすげ替えさせられている。
特にUAC関連では叩かれることの多いMSだけれども、今回はその件をとやかく言うつもりはない。

今回、Visual Studioを使ってちょっとしたインストーラを作っていて気づいたことがありました。インストーラをデバッグ実行してもデバッグできなかった。理由は上のとおりで、Visual Studioから起動されるプロセスはVisual Studio自体のユーザー特権で実行されるから、Program Filesへの書き込みが禁止されているという訳だった。これまでは、ほとんどの開発をXPでやっていたから気づかなかったし、またReleaseビルドのときは親インストーラである”setup.exe"からの子プロセスとして実行されるインストーラだったので、特権レベルが"インストーラ"レベルに昇格されていて問題なく動いていた。が、開発環境を7で、Visual Studioから直接起動するとこの特権問題が露見した、という具合だったようです。

で、回避策として、Debugフォルダにあるデバッグ対象のインストーラ.exe自体を、エクスプローラ上からプロパティ変更して「管理者として実行する」に変更してみた。すると・・・、

Visual Studioはデバッグセッション開始時に、デバッグ対象.exeが「管理者として実行する」ことを要求していることを自動的に検知してダイアログを表示し、デバッグ処理を継続させるとVisual Studio自体が「管理者」権限で再起動した。

残念ながらそのままデバッグは開始しなかったけれど、この段階ではもうVisual Studioは管理者で実行されているので、再度F5でデバッグを開始すると難なくデバッグプロセスが開始され、以降ステップ実行やら変数インスペクションができた。もちろんインストーラとしてProgram Filesフォルダへのアクセスも自由にできている。

なかなか気が利いている、と思った。
しかし、起動している子プロセス(というかexeファイル)が管理者権限での実行を要求している、ということはどうやって検知しているのだろう?

2011年4月23日土曜日

Windows7 SP1・・・もうリリースされてたの?

今になってSP1が出ていることに気がつきました。だって、Windows Updateは教えてくれなかったし・・・。
いや、ネットで調べてみるとWindows UpdateがSP1を自動検出してインストールされるはず、となっているのだけれど、いま使っているこのLet's Note S9では何度Windows UpdateやってもService Pack 1という文字が出てこなかった。なんでだろう?

と思って調べてみると、どうもS9で使っているビデオドライバのバージョンによってはWin7のSP1が検出すらされないらしい。いったいどういう因果関係になっているのかまったく理解不能だけれど、パナソニックのHPの情報によると、ビデオドライバのバージョンが8.15.10.2104あたりだとダメらしく、2011年3月16日に公開された8.15.10.2281を入れるとうまくいくと書いてある。

で、まずこのビデオドライバをインストールしてみた。そしておもむろにWindows Updateすると、あぁ、確かにService Pack 1という文字が出た!

というわけで、いまはそのSP1が入ったLet's Note S9から書いているわけだけれど、こんな重要な情報は教えてくれないなきゃダメでしょう?>パナソニックさん。
というか、ドライバのアップデートなんかは普通は自分で調べてやるかやらないかを決めるモンだとは思いますが、付属している「PC情報ポップアップ」という(妙な)ソフトが入っているのなら、「修理をご依頼されるお客様へのお願い」なんてしょうもないお知らせを流すよりも、こちらの方が重要だと思うんだけどなぁ。「修理をご依頼される~」は、実際に修理をお願いするときに直接お客さんに伝えた方がよっぽど意味があると思う。

2011年4月20日水曜日

Stellarisのバグ?

らしきものを発見!
と言ってももともとエラッタの多いCPUなので驚かないけどちょっとハマったのでメモ。

uDMAをping-pongタイプで使用するときは注意が必要。

もしDMA動作を中断させる場合はDMAENSETの対応ビットをクリアすればよいのだが、再度DMAを開始するときはPrimaryとAlternateをソフトウェアで変更してはダメ!

DMAALTSET/DMAALTCLRというレジスタでソフトウェア的にPrimary側のDMAレジスタを使うか、Alternate側のレジスタを使うかを選択できる訳だけれど、ping-pongモードを使用してこのレジスタを変更した場合、次のDMAリクエストでDMA転送は行われるけれどDMA転送完了時に割り込みが発生しない、という問題が生じている。
具体的に以下の手順。

  1. ping-pongモードでDMA転送を行うよう設定する。
  2. DMAリクエストが発生して、次のDMA転送がAlternate側(DMAALTSETレジスタのビットが'1')となっているときに何らかの理由でDMAを中断させたとする(DMAENACLRに'1'を書く)
  3. 再度DMA転送を開始するために、DMAALTCLRに1を書いてPrimary側を使うよう初期設定し直したのち、Primary側/Alternate側のChannel Control Structureを正しく設定する。
  4. 次にDMAリクエストを発生させると、現在選択されているPrimary側のChannel Control Structureを見ると確かにXFREMODEがping-pong(=3)からstop(=0)になっていることが分かる。しかし、割り込みは発生しない。
  5. このとき、DMAALTSETレジスタはセットされている(ping-pongモードなのでuDMA自身がPrimary/Alternateを自動的に切り替える)。引き続き、次のDMAリクエストを発生させると、今度はAlternate側のXFREMODEがSTOPに変わると同時に割り込みも発生する。
このことから、どうやらDMAモジュール内部では割り込み関係に関してPrimary/Alternateを選択する内部レジスタのようなものがあるが、それはDMAALTSET/DMAALTCLRとは連動していないのでソフトウェアから変更したAlternateの選択状態をうまく反映できていないようだ。

回避策としては、ping-pongモードを使う場合、DMAALTSETレジスタが示す現在のPrimary/Alternateの選択状況に応じて、Primary→Alternateの順か、Alternate→Primaryの順のどちらかでDMA転送を行うようChannel Control Structureを使い分けなければ行けない。

2011年4月15日金曜日

Visual Studioでビルドした実行コードが・・・

なんか肥大したなぁ。

元は1800KBytesくらいだったはずだが、少し変更してビルドするだけでいまは3000KByteを超えている!そんなに変更してないんだけどな~。テンプレートを追加したわけでもないし。
心当たりと言えば・・・

もしかして、昨日のWindows Updateですかね?そういえば、Visual Studioの再頒布可能パッケージがどうのこうの出てたっけ?

と思って昨日のMS11-025を調べてみると・・・、ありました!KB2465361ですな。ここには脆弱性の改善だのなんだのとかしか書いてなくて、ライブラリが大きくなったとは書いてないな。でも、ネットでしらべるとWin2Kで動かなくなっただの、実行ファイルが大きくなっただの、そういう書き込みがあるので原因はこれだろう。
ってか、本当にこれが原因かどうか調べたいんだけれど、KB2465361のアンインストールの仕方が書いてない。ちょっと調べようがないな~。TrueImageで2、3日前に戻せば分かるんだろうけど、普通そこまでしないよな~。

というわけで、とりあえず動いているようなのでモーマンタイとする(悪)。というより、MFCのスタティックリンクしている時点で“アウト”なのかも・・・。

2011年4月11日月曜日

初IKEA

IKEAに行くのが初めてってわけではなく、相方(=妻)と話のタネに何度か行ってみたり、何かを買うつもりで行ったりしたこともあるのだけれど、いつも「何となく欲しいものがないな」「ピンとくるものがないな」ということで手ぶらで帰ってくるだけだった。が、今回はどうしてもチェストと本棚を買うつもりで出かけました。

先日の引っ越しでIKEAまでの距離は遠くなったものの、高速道路の接続が良くなったりで1時間かからずに行けるようになったのは良かった。
いつものようにまずはコーディネートされたショールームのような部屋を回りながらイメージを膨らませていくわけだけれど、どれも上手にまとめられていて「いいね」と相方と話すものの「欲しいな」と思うところまではいかない。私たちは「何でも良い」とか口では言うものの、やはり人とは一ひねり違う「何か」が欲しいがための何か物足りなく、でもその「何か」を具体的に説明できるほどの想像力と申しますか、妄想力もなく、お店側としては一番やりにくい客のタイプではないかと。

それはさておき、今回は必要に迫られてきているわけだし、多少満足できないorしっくりこない部分があっても、それはそれで「妥協する」ということも覚えなければいけないわけで。まあ、その点ではスウェーデンハウスを建てるにあたって妥協の連続を克服してきた私たちですから、今回は何とか折り合いをつけられるくらいには成長しているはず。
そして、とりあえずといっては何だが、チェストを2種類と本棚を(何とか時間切れの感もあってか)チョイスすることができて、カートを押して別フロアの商品棚に移動。そう!「初IKEA」というのは、これまで何度か足を運んでおきながら、「初めてIKEAで買い物をする」という意味だったのです。
自分の欲しい商品があの巨大な棚の上の方に置いてあったらどうやって下ろすのだろう、とか思いながら見て回っていましたが、なるほど縦方向はすべて同じ商品で上部の棚は在庫分なのだと、今更ながら納得。
で、まずは目的のチェストと同じ番号を発見して、カートに移動させる。がっ、やけに重いではないか!まあ成人男子としては筋力はない方ではないので、フンッ!と踏ん張ればカートに乗せ替えるのはそんなに大変な訳ではない。そして、もう1種類のチェストも見つけてカートに移動。こちらはさほど重くない。
最後に本棚を探す。売れ筋商品だけあって、色のバリエーションの他、付属品も多く探すのにちょっと時間がかかったが棚の前に到着。この棚は高さが2mちょいあるのである程度重いということは予想していたが、積んである商品の一つをちょっと手前に滑り出させるだけでも一苦労。ここで初めて気がついた。箱の横に重さが書いてある。「なっ、なにぃ~、39Kgぉ~!!」

まさかそんなに重いものとは思っていなかった。だって、IKEAはDIY商品、自分で組み立てるから安く上がるのがウリだと思っていたから、まぁ普通の健康な成人なら持って帰られるくらいのものかと思っていた。39Kgというとちょっと力のある人でも気合いを入れないと持てない重さではないか。まして体力のない人なら男子でもまずムリ!あーなるほど、先ほどすれ違った家族連れが若夫婦だけでなくその父親も連れてきていたのはそういうことか。確かに見回してみると、ベッドを持って帰るのに父と息子らしき男性が二人で必死になって棚からだそうとしているな。
と感心している場合ではない。この39Kg、しかも2mもの長尺の物体をどうやって持って帰るか。妻は成人といえども女性だし、39Kgの片側をかかえることは難しそうだ。まして、今日は乳飲み子をかかえている。。。相談して、今日のところは本棚は諦めることにした。
この本棚、特に気に入ったというほどでもないけれど、値段的には割安だし、新居に本棚が必要だし、それにようやく妥協点を見つけて買うと決心したところなのに、こんな理由で買って帰れないとなると今度はむしろ買えないことが悔しく思えてくるのはなぜだろう?
まあ、今回買って帰るチェストも結構重かったし、私の老体のことも考えて潔く諦めることにしよう。レジの方に向かいつつ、ちなみにこのチェストの重さがいったいどれくらいなのか?と思ってカートの横を見てみると・・・「な、なにぃ~、35Kgですか!」。そりゃ重いはずだ。
というか、私はいつの間にか35Kgものものを運んでいたんだな。しかも2つも!となると、先ほど諦めたばかりの本棚39Kg、ここまで重くなれば4Kgくらいの差は目クソ鼻クソ五十歩百歩ではないかと思えてきた。やっぱり買って帰ろうかといろいろと思考するも、やはり重さだけでなく、2m超という長さも侮れないな~。一人でバランスよく持つことを考えると、チェスト35Kgの1.5倍くらいの重さ=50Kg超のものを持てるくらいの怪力の持ち主でなければ持てないかもしれない。そう思うと、やはり今回は諦めて誰か男手をもう一人つれて来るしかないと、自らを納得させるのであった。

というわけで買い物終了。これだけのものを買って2万円台で済むのだから安いのは安い!しかし、もう身体が痛くなってきた・・・。先日お世話になったばかりだけれど、本当に引っ越し業の方や配送業の方の体力はある意味「特殊技能」だなぁ・・・感謝感謝。