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

2015/07/27

VisualStateManagerのVisualStateを確認する

WPFのコントロールの遷移をVisualStateManagerでデザインしていると、現在のVisualStateを直接確認したいときがあります。

VisualStateGroupが一つだけなら同じVisualStateGroup内のVisualStateは排他なので結果を見て判断することも難しくないですが、VisualStateGroupが複数になると異なるVisualStateGroup間のVisualStateは併存するので、複数のVisualStateの効果が重なることになり、何がどうなっているか判断に迷うことがあります。というか、かなり迷いました。

そこで、デバッグ用に現在のVisualStateを出力する添付プロパティを書いてみました。
このキモは、VisualStateGroupは対象のコントロール自体ではなく、たいていその中のGridなどに定義されるので、子要素を下りながら探す必要があることです。なお、VisualStateManagerではなくトリガーに依っている場合は、当然何も出てきません。

VisualStateの変化を捉える方法が見つからなかったのでIntervalで指定された秒ごとに永久ループで出力しますが、そこはあくまでデバッグ用ということで。

これをToggleButtonに使った例。
このToggleButtonにはCommonStatesとCheckStatesのVisualStateGroupがあり、これらがToggleButtonと継承元のButtonBaseとさらに元のControlのChangeVisualStateの重ね掛けで変化するというややこしいものですが、一番下に加えたVisualStateMonitorの添付プロパティは以下のように出力します。
// 初期状態
Element: TestToggleButton -> Group: CommonStates -> State: Normal
Element: TestToggleButton -> Group: CheckStates -> State: Unchecked
// カーソルを上に移動
Element: TestToggleButton -> Group: CommonStates -> State: MouseOver
Element: TestToggleButton -> Group: CheckStates -> State: Unchecked
// クリック
Element: TestToggleButton -> Group: CommonStates -> State: MouseOver
Element: TestToggleButton -> Group: CheckStates -> State: Checked
// カーソルを上から外す
Element: TestToggleButton -> Group: CommonStates -> State: Normal
Element: TestToggleButton -> Group: CheckStates -> State: Checked
// 再度クリックしてカーソルを外す
Element: TestToggleButton -> Group: CommonStates -> State: Normal
Element: TestToggleButton -> Group: CheckStates -> State: Unchecked
これが分かれば簡単になるというものでもないですが、直接確認しながら作業できるのは随分違うと思います。実はもっと簡単にできる方法があるのかもしれませんが、とりあえず。

[追記] 添付プロパティの作成について

添付プロパティの構文の話ですが、依存関係プロパティの場合、PropertyMetadataのPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)のdはそのプロパティが定義されたクラスのインスタンスなので、これをキャストして処理をインスタンスメンバーに繋げることができます。一方、添付プロパティの場合、dはそのプロパティが添付されたFrameworkElementのインスタンスになるので、添付プロパティが定義されたクラスのインスタンスメンバーにそのままアクセスはできません。

したがって、FrameworkElementのインスタンスの処理はおおよそ以下に分かれます。
  1. 静的メソッドの中で処理を完結させる。デリゲートを駆使すればある程度複雑なこともできるが、状態をフィールドなどに保存できないので、行き届いた状態管理は難しい。

  2. 静的メソッドの中で、状態を静的フィールドなどに保存しながら処理する。アプリ中の一カ所でしか使われない場合や、静的な値が共有されても構わない場合に限られる。

  3. 添付プロパティの型をそのプロパティが定義されたクラスにすると、初期化時にe.NewValueにそのクラスのインスタンスが入ってくるので、それをキャストしてインスタンスメンバーに繋げる。WindowChromeで使われている方法で、複雑な処理も行いやすい。
あくまで構文上のテクニックですが、結構悩むところなので。