2015/10/05

ThinkPad X230のパームレスト

ThinkPad X230のパームレストの互換性について。

1. 修理上がり


先日、X230をLCD関係の修理に出したら、キーボードとパームレストにも問題が見つかったとのことで、一緒に交換されて帰ってきたのは有り難いのだけど……。
ThinkPad X230: Palmrest

一見どこもおかしいところはないように見えて、自分でも「何か違う気がするけど、まあいいか」と思ってやり過ごしたのだけど、X230を持っている人なら気付くかもしれない。

そう、これパームレスト(キーボードから手前の筐体)がX220用のものになっている。X230のものはキーボードに向かって下がる部分が直線的に折れた平面になっているが、これはなだらかな曲面になっている。

そのくせこれだけ見ると特別おかしくは見えないのは、元々X230が機構的にはX220のキーボードを変更したマイナーチェンジモデルだから。細かく見れば、クリックボタンの横になる部分に、X220のクリックボタンにある曲線のラインにつながる微かな曲線が入っているが、ぱっと見には分からない。

で、そうと分かった後も、機能的には問題ないし、「これはこれで面白いから、このままでいいか」と思っていた。初めのうちは。

そのうちキーボードの右端手前と左端手前が妙にぺこぺこ上下に動くことに気付いて、キーボードを取り付け直したり、果ては両面テープで留めたりしたりして、これはどうもおかしいと思い始めて分解写真を見比べているうちに気付いた。X230とX220のパームレストには互換性があるように見えて、そうではないことを。

2. パームレストの違い


結局、本来のX230用のパームレストを送ってもらって自分で交換することにしたので、そのときに比較してみた。上がX220用で、下がX230用。
ThinkPad X230: Palmrest Comparison

注目すべきはキーボードとパームレストの境目になる部分にある穴で、(角の部分を除いて)左からキーボードの爪が嵌まる穴、キーボードにかかった液体の排水口、爪の穴、クリックボタン部分を挟んで、爪の穴、排水口、爪の穴がある(爪の穴は計4箇所)。この構成と位置は同じだが、よく見ると穴の大きさが違う。
Thinkpad X230: Palmrest Comparison

排水口はほぼ同じ大きさだが、爪の穴は上のX220用は縦幅が広いのに対して、下のX230用は縦幅が狭い。

このこと自体はX230が出た当初から言われていて、X230にX220以前のキーボードを装着しようとしたときの物理的障害になっている。
X220以前のキーボードは金属板の縁が上に立ち上がった浅いバスタブ状になっていて(排水口に面した部分だけ開いている)、爪部分も同じなので高さがある。これに対してX230のキーボードはバスタブを止めたので、金属板がそのまま真っ直ぐ爪になっている。
Thinkpad X230: Keyboard
ThinkPad X230: Keyboard

したがって、X230用のパームレストの爪の穴は縦幅の狭い穴でよく、これにX220以前のキーボードの爪を嵌めるには穴を上に削って大きくする必要がある(あえてその方法を選ぶなら)。逆に、X220用のパームレストの爪の穴にはX230のキーボードの爪はそのまま余裕で通る。

それ以上深く意識してなかったが、そこが実は落とし穴で、キーボードの固定ネジは中央部の2箇所しかないので、左右の端は爪だけで固定される形になっている。キーボードを取り付けるときは初めに奥に差し込んで奥側の金属板をキーボードベゼルに引っ掛けた後、手前に引いて手前側の爪をパームレストに嵌める構造になっている。このとき奥側に引っ掛けた部分でキーボードがわずかに反るテンションが掛かっているので、手前側の爪もぴったり嵌まっていれば強固に固定されるが、ここに遊びがあると手前側が浮き気味になって、タイピング時にぺこぺこ動いてしまう。

したがって、もしX230にX220のパームレストを流用する必要が出たときは、キーボードをきっちり固定するためには爪の穴を小さくする工作が必要、という話なのだが、この情報がいつか誰かの役に立つことがあるのかどうか。

3. おまけ


キーボードの爪がバスタブ状でなくなったことで液体が爪の穴から浸入しやすくなったのでは、と思うかもしれないが、そこは抜かりなかった。パームレストを裏から見ると、
Thinkpad X230: Palmrest Comparison

上のX220用では爪の穴がそのまま抜けているのに対して、下のX230用では爪の穴の奥がシールで閉囲されていて、液体がそのまま中に入らないようになっている。

とはいえ、この排水機構自体、とくに密閉されているわけでもなく隙間も多いので、気休め程度ではあるけど。

2015/09/24

ReadyNASのファン交換

ReadyNAS Ultra 2のファンを初めて掃除して以来、時々掃除してきたが、ついに掃除しても軸音がするようになったのでファン交換を決意した。

1. 失敗のファン


Ultra 2の購入時にファンがうるさかったときのために交換用ファンも買っていたが、元のファンが十分静かだったためにお蔵入りになっていた。この出番がついに来た、というわけで、それに交換すれば終わりの予定だった。

そのファンはオウルテック扱いの山洋電気製SF9-F1で、静音的には鉄板のものだったが……一応温度を見ているとCPU温度がUltra 2では未踏の80度を超え(RAIDar Protocolによる上限は80度)、冷却力不足が露呈した。さすがに回転数が800RPM固定では無理があったらしい。「こんなこともあろうかと……」が実は保険になってなかったという。

仕方ないので、直径9cmで回転数可変のファンということでENERMAXのPWMのUCTB9Pを買ってきたが、これが完全な外れ。たぶん不良個体だと思うが、軸音が元からひどくて話にならない。初ENERMAXだったのだが、不幸な出会いだった。

2. 元のファン


気を取り直して元のファンを改めてチェックすると、このDeltaのAFB0912HHは日本国内の一般の取り扱いはないらしい。ならばと大体同じ筐体の312のファンを見てみると、同じ型番のものだった。
ReadyNAS Ultra 2 and 312: Fans

左がUltra 2のもの、右が312のもので、型番は全く同じ。312本体のコネクタも当然Ultra 2と同じ3ピンのもの。
ReadyNAS 312: Fan Connector Pin

調べてみると4ベイのモデルも同じファンらしいので、ReadyNASの2ベイと4ベイのシリーズでずっと使っているものっぽい。312のファンもこれまでのところ良好なので、これが手に入れば話は早いのだが、サブタイプが複数あって該当するものが分からないし、送料もそれなりにかかる。

3. Noctuaのファン


他に探すとして、3ピンということは4ピンのPWMと違って電圧で回転数を制御していることになる。SF9-F1で回転数が固定だったのは電圧にかかわらず回転が一定になるようにできているのだと思う。そういう場合を除けば選択肢は広い。

一方、静音的には通常時800RPM程度がターゲットなので、ほとんどが定格で1000RPM台半ばのファンのスペックを見たところで参考にはならず、結局は勘になる。回転数の変わる範囲内で、軸音(直接の風切り音以外の音)が出る回転数があるかは実際に回してみないと分からないし。

ということで、一応回転数可変という点を考慮して、NoctuaのNF-B9 redux(非PWM)を購入。回転数変更用の抵抗など付属品を省いたモデルだが、不要なので。

左下が元のDelta、右下が山洋電気、左上がENERMAX、右上がNoctua。
ReadyNAS Ultra2: Replacement Fans

ファンの羽、コアの支柱ともバラエティに富んでいるが、通常時無音を目指すレベルの静音ではあまり関係なかったりする。

NF-B9の結果は当たりで、通常時は十分静かだし、回転数が上がっても気が付くような軸音は発しない。ファンを探す旅も3つ目で終わった。願わくば、次に交換が必要になったときにも市場に存在していることを。

2015/08/23

ReactivePropertyと無線LAN

ReactivePropertyと無線LANという組み合わせでアプリを作りました。

1. WLAN Profile Viewer


先に簡単にアプリの紹介を。無線LANプロファイルを管理するためのWindowsデスクトップアプリです。ストレートにWLAN Profile Viewerという名前にしました。

Windows 8以降、OS標準の無線LANのUIは現在電波の入っている無線LANを入口にしたものになりました。これは初めて接続するときだけ接続先の無線LANを選んで、後は自動的に接続するようOSにお任せにしてしまう前提であればシンプルでいいですが、ユーザーが自分でコントロールしようとすると必ずしも使い勝手のいいものではないです。今時コーヒーショップで無線LANを見ると20も30も電波が飛んでますし。

このアプリは既に作成された無線LANプロファイルを電波状態を含めて一覧表示し、そこから接続と切断ができます。これにプロファイルの順番の変更(ただしWindows 10では意味なし)と削除も併せて最低限一通りの管理ができるので、OS標準のUIよりは多少使い勝手がいいと思います。

正直いえばReactivePropertyの練習用に作っていたものですが、意外と実用性がありそうなのでアプリに仕立てました。詳しくはレポジトリで。

2. ReactivePropertyのプロパティ


ReactiveProperty自体については開発者の@neueccさん、@xin9leさん、@okazukiさんがたくさん書かれてますので、そちらを読んでいただければいいのですが、「そもそもReactivePropertyのプロパティって何?」という点について、「そこからか?」と言われそうですが、自分は何だろうと思ったので簡単に書きます。

これは最初に@neueccさんが書かれてます。正確にはそちらを。
ざっくりと自分のイメージでいえば以下のような感じです。
まずプロパティはデータを入れておく入れ物といっていいと思いますが、そのためには内部に値を保持していて、それに随時アクセスできる必要があります。一方、IObservableのチェーンではイベントなどで値が流れていくわけですが、常に値があるとは限らないので、そのままではデータの入れ物にはなりません。

そこで内部のlatestValueフィールドに値を常に保持するようにし、それにValueプロパティを通してアクセスできるようにしたのがReactiveProperty、のベースなのだと思います。したがって、プロパティを利用する立場からは、相手は一義的にはValueプロパティで、latestValueフィールドはそのバッキングストア、それらを提供するコンテナがReactivePropertyと見なすことができ、それをIObservableのチェーンに挟み込むことで(発端でも終端でも構いませんが)、両者をうまく融合させたと。

というわけで、ReactiveProperty自体はValueプロパティのコンテナなので、基本的にReadonlyなプロパティとして生成すればいいわけです。なお、ReadOnlyReactivePropertyはそのValueプロパティがReadonlyという意味で、Readonlyの対象が違うのですが、初めは混同してました。

3. ObserveElementXXXXX


ObserveElementXXXXXはコレクション要素のプロパティ変化をIObservableにする拡張メソッド群です。自分がコレクション要素のプロパティ変化をReactivePropertyで捉えるにはどうすればいいかと書いてたら、@xin9leさんと@okazukiさんがあれよあれよという間に作り上げて、すげーと思いました。

この使用例として、要素になるものとして以下のMemberViewModelがあるとします。この中のIsLongとIsSelectedはModelと同期、あるいはViewにバインドされて随時変わるものと考えてください。
これを要素とするObservableCollectionを持つMainWindowViewModelは以下のとおり。
この中でCLRプロパティであるIsLongにObserveElementPropertyを、ReactivePropertyであるIsSelectedにObserveElementObservablePropertyをそれぞれ繋げています。これらの戻り値の型はPropertyPackで、このValueに元のプロパティの値、Instanceにその要素のインスタンスが入っているので、まずValueを見てtrueのものだけ通し、その後で要素のインスタンスをメソッドに渡しています。

コレクション要素の変化を追うのは割と面倒ですが、それがこれだけでできます。なお、同じ結果は、適当なタイミングでコレクションをループして走査することでも得られますが、そうすると当然ループのコストがかかります。

また、プロパティ変化をある条件でフィルターした要素を集計したいときは、FilteredReadOnlyObservableCollectionが利用できます。この例は以下のようなものです。
ReactivePropertyであるIsAllLongを生成するのに、上の方法ではIsLongにObserveElementPropertyを繋げ、その後で元のコレクションをループさせて判定しているので、当然ループのコストがかかります。対して、下の方法ではIsLongがfalseである要素のコレクションをFilteredReadOnlyObservableCollectionで生成し、このCollectionChangedイベントを捉えて、元のコレクションとFilteredReadOnlyObservableCollectionのCountから判定しています。

ただ、これが使えるのは対象の、変化するプロパティがCLRプロパティの場合で、ReactivePropertyを対象とするときは別の方法を考える必要があります。

単純には、ObserveElementObservablePropertyを繋げ、そこから拾い出した要素を外のコレクションに保持し、その要素数を使えばいい気がしますが、これだけだと元のコレクションから要素が削除された場合に追い切れません(ObserveElementObservablePropertyは現在コレクションにある要素を対象とするので)。これを考慮した場合は以下のようなものが考えられます。
ReactivePropertyであるIsAnySelectedを生成するのに、上の方法では元のコレクションをループさせて判定しています。対して、下の方法では、いきなり膨らみましたが、条件を満たす要素をListに保持するようにし、現在コレクションにある(新たに追加された場合も含む)要素をObserveElementObservablePropertyで、要素が削除された場合とコレクションがクリアされた場合をCollectionChangedAsObservableで追うようにした上で、ListのCountで判定しています。

一応これを汎用の拡張メソッドにしてみたものが、他の例と併せてReactivePropertyTestプロジェクトにあるので、興味があれば見てください。ちなみに、NewItemsをチェックしているルートは実際はObserveElementObservablePropertyが先に捉えるので不要ということに後から気づきました。

しかし、変化の頻度が少なく要素数も特に多くなければループさせる方法でも十分なので、実際ここまでやることはないかもしれません。

4. まとめ


このアプリではM-V-VM間を専らRxとReactivePropertyで結ぶようにした結果、その部分のコードがとても少なく、すっきりとなりました。

したがって、Rxをガンガン使うようなアプリであれば、ReactivePropertyも併用すればその威力はさらに増すと思います。Rx自体の学習コストが決して低くない問題はありますが。ReactivePropertyのソースにはRxの高等テクニックが詰まっているので、時々見ると発見があります。

最後に一つ。RxとReactivePropertyを駆使すればアプリ内を融通無碍に繋ぐことができますが、これと非同期が組み合わさると実行コンテキスト(スレッド)がよく分からない状態になることがあります。何か動作が妙……というときは実行コンテキストを確かめてみるのも手です。