ドナドナされるプログラマのメモ

Windows用アプリのプログラミングメモ

Factorio Space Explorationメモ:貨物ロケットの自動準備

FactorioのSpace Exploration Modをプレイしていると、そのうち母星の資源が枯渇してくる。そうなると他の惑星へ資源を奪いに行く必要がある。Factorioの醍醐味は様々な工夫による作業の自動化にあるので、できれば奪った資源の輸送も自動化したい。しかし、ここで問題になるのは貨物ロケットの発射準備方法。貨物ロケットを発射するには、発射台に以下をセットする必要がある。

  • 宇宙カプセル×1
  • 貨物ロケットセクション×100
  • 液体燃料×(目的地までの距離により異なるらしい)

このうち、液体燃料は生産・貯蔵設備からパイプを発射台に繋げておけば勝手に必要量だけ重点されるので楽なのだが、残りの2つが曲者。発射台にはロケットに載せる貨物もセットできるのだが、貨物のセット先も上記ロケットの部品のセット先も共通なのだ。そのため、何も考えずにインサーターで宇宙カプセルや貨物ロケットセクションを発射台にセットする構成にすると、必要量以上がセットされてしまい貨物ロケットがこれら部品で埋まってしまう。そのため、以下の仕掛けが必要になる。

  • 発射準備中であることを認識する
  • 発射準備中ならば部品を必要量だけセットする

発射準備中かどうかは発射台から出てくる信号で識別ができ、0が準備中、1が準備完了である。またFactorioでは信号の比較、信号の加減乗除ができ、ラッチ回路等も作れる。以上の道具立てからすると、実現方法は・・いくつもあるなあ。思いついた各方式の概要を列挙してみる。

  • 1->0の信号変化をトリガーに所定の個数をセットする。
  • 1のときはセットせず、0の間だけ所定の個数をセットする。
  • 宇宙カプセルを1個だけセット後、信号が1になるまで貨物ロケットセクションをぶちこむ。
  • 打ち上げに必要な部品はセットと同時に消滅する(発射台に吸収される)ことを利用し、各部品をひたすらセットするインサーターとひたすら取り除くインサーターを設置する(信号は使わない)

・・・あれ?ラッチとかが必須かと思ったけど、一番最後のをちょっぴり改良して、セットするインサーターを発射台からの信号で直接制御してあげれば行けるんじゃない?つまり、こうだ。

f:id:donadonasan:20200526180659p:plain

ひたすら部品のセットと除去をする例(失敗)

でも、これは失敗。セットする方にスタックフィルタインサータを使ったにも関わらず、なぜか普通のフィルタインサータが全てを瞬時に除去してしまうのだ。

ということで、他の方式を検討する。まずラッチ方式だが、リセット信号のエッジ検出がめんどくさそうなのでパス。そこで、信号が1の間は部品をセットせず、信号が0の間だけ所定の個数セットする方式にする。所定の個数をカウントする回路のクリアは、信号=1を条件とすればタイミングチャート的に大丈夫なはず・・。

f:id:donadonasan:20200526184841p:plain

3個だけインサータでセットする場合のタイミングチャート

などと思いながら回路を組んでいたら、発射台からセット済みアイテムの個数も出力されていたことが発覚。ラッキー。(でも考えたり記事書いたりしたのにかかった数時間は無駄に)

f:id:donadonasan:20200526191705p:plain

発射台からの出力信号例。Lは発射先までの距離、Eは空いている貨物スロット数、Fは埋まっている貨物スロット数

 

Factorio スペース・エクスプローラーでのアイテム制作メモ

Factorio スペース・エクスプローラーでディープ・スペース・サイエンス・パックを含む各種パックを作ろうとすると、拡張総合~カタログのレイヤーまではそれぞれが絡み合っているため必要な工場数がよくわからない。そこで、毎秒1個生産する場合に必要な工場の数を整理した。コンピュータは量子スーパーコンピュータを基本とした。

 

  流速 設備 設備数
天文学サイエンスパック 1 宇宙工場 3
生物化学パック 1 宇宙工場 3
エネルギー科学パック 1 宇宙工場 3
材料科学パック 1 宇宙工場 3
ディープ・スペース・サイエンス・パック 1 宇宙工場 2
重要なデータ 24 量子 288
天文学インサイト 44.2 ニューラル 24
生物学的インサイト 44.2 ニューラル 24
エナジーインサイト 44.2 ニューラル 24
材質インサイト 44.2 ニューラル 24
拡張総合天体カタログ 2.02 ニューラル 27
拡張総合生物カタログ 2.02 ニューラル 27
拡張エネルギーカタログ 2.02 ニューラル 27
拡張総合材質カタログ 2.02 ニューラル 27

 

重要なデータ・・まじか・・

 

Factorioでアイテム生産に必要な工場の数を試算するツール(FactorioFactoryCalculator)

Factorioでアイテム生産に必要な工場の数を試算するためのツール, FactorioFactoryCalculatorを公開しました。これを使えば、例えば製造サイエンスパックを毎秒3個作るために必要な、生産力モジュール1の工場の数やそのための発展基板工場の数などが試算できます。

  • プロジェクトページ

github.com

  • バイナリ公開場所

Releases · nibasya/FactorioFactoryCalculator · GitHub

重い処理中にステータス(状態)を表示するダイアログの実装(ユーザーインターフェーススレッド)

解決したい課題

MFCを使ったソフトにおいて重い処理をしている最中に、進捗等を表示するダイアログを表示したくなることは多々ある。しかし、単純にダイアログを作って表示しようとしても、以下のようにうまく行かないことが多い。

  • 重い処理が終わるまでダイアログが表示されない
  • ダイアログは表示されるが、表示内容が更新されない

何が原因か

これらが起きるのは、今の処理が終わらないとダイアログを表示するための処理が実行されないためである。ダイアログの表示やコントロールの変更を処理するメッセージはダイアログのメッセージプロシージャで処理されるのだが、困ったことにこのメッセージプロシージャは、デフォルトではメインウィンドウのメッセージプロシージャに接続されてしまうようなのだ。そのため、メインウィンドウのメッセージプロシージャが重い処理によって応答しなくなった場合、ダイアログの方も応答が止まり上記の症状が発生する。

一般的な解決方法

一般的な解決方法は、重い処理を別スレッド化してしまい、メインのメッセージプロシージャの応答性を確保することである。すなわち、AfxBeginThread()を使ってワーカースレッドを生成し、そのスレッド上で重い処理を実行する。AfxBeginThreadの第1引数はワーカースレッドの関数、第2引数はその関数への引数である。多くの場合、第2引数はワーカースレッドに渡したいオブジェクト、すなわちその関数が属するオブジェクトへのポインタである。そしてメインのウィンドウまたはダイアログでは、適宜ワーカースレッドの状態などを覗いてGUI上の表示を更新する。

Example of creating worker thread

この方法にて注意すべき点は、通常はワーカースレッドからはメイン側のMFC関数を直接呼び出せないことである。例えば、SetWindowText()を直接呼び出すことはできない。これは、ワーカースレッドにはMFCオブジェクト生成時に自動保存されるHWND情報が無いためである。詳しくは以下を参照。

マルチスレッド: MFC プログラミングのヒント | Microsoft Docs

そのため、前述のようにダイアログ側から適宜覗いて表示を更新するか、上記リンクにあるようにPostMessage()等を使ってメイン側ウィンドウに表示の更新を促すメッセージを送る必要がある。なお、上記リンクには FromHandle()を使ってメイン側オブジェクトへのポインタを取得してアクセスすることも書かれているが、これによりアクセスできるのは各MFCオブジェクトであり、MFCから派生したユーザー定義のクラスではないため実用的ではない。

ワーカースレッドに処理を移せない場合の解決方法

「重い処理」の内容によっては、処理をワーカースレッドに移すことができない。たとえば、CTreeCtrlに数万、数十万のノードを追加する処理は非常に重いのでワーカースレッドに移したくなるが、前述の通りワーカースレッドからはメイン側のMFC関数を呼び出せないため移せない。

このような場合は、例えばメインウィンドウとは別のスレッドを生成し、ダイアログのメッセージプロシージャをそちらのスレッドに接続すればよい。このようにすれば、メインウィンドウは重い処理のために応答しなくなるが、状態を表示するダイアログは応答を継続できる。MFCにおいて、メッセージプロシージャを有するスレッドはユーザーインターフェーススレッドと呼ばれ、単純なスレッドであるワーカースレッドとは区別される。ユーザーインターフェーススレッドもAfxBeginThreadで生成できるが、ワーカースレッド生成時とは引数が異なり以下の引数の方を使う。

CWinThread* AfxBeginThread(
  CRuntimeClass* pThreadClass,
  int nPriority = THREAD_PRIORITY_NORMAL,
  UINT nStackSize = 0,
  DWORD dwCreateFlags = 0,
  LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

使用例は以下の通り。

CStatusDlgThread* pThread = DYNAMIC_DOWNCAST(CStatusDlgThread, AfxBeginThread(RUNTIME_CLASS(CStatusDlgThread)));

ここで、CStatusDlgThreadはCWndThreadクラスの派生であり、ユーザーインターフェーススレッドの実体である。また、DYNAMIC_DOWNCASTは単なるdynamic_castでもよいはずだが、ここではMFCの流儀に則っている。

作成したユーザーインターフェーススレッドにダイアログのメッセージプロシージャを接続するには、CWndThread::m_pMainWndにダイアログへのポインタをセットすればよい。

ダイアログに表示する内容を変更する場合は、メインウィンドウからダイアログに対しユーザー定義のメッセージをSend / PostMessageする。

状態を表示するためのエディットボックスと処理を中止するキャンセルボタンを有するダイアログの実装例を以下に示す。この例では、以下のような構成となっている。

  • CFactorioFactoryCalculatorDlg:メインウィンドウのダイアログ。OnCbnSelchangeComboTarget()が呼び出された際に、AddNode()を呼び出してCTreeCtrl m_CtrlTreeに大量のアイテムを追加する。追加処理中に状態表示ダイアログを表示し、ユーザーに進捗を示す。
  • CStatusDlg:状態表示ダイアログ。ユーザー定義のメッセージ:ID_UPDATE_STATUSを受け取ると、メンバ変数:m_Statusにセットされた文字列をエディットボックスに表示する。
  • CStatusDlgThread:状態表示ダイアログのユーザーインターフェーススレッド。状態表示ダイアログを所有し、InitInstance時にDoModal()を呼び出す。ダイアログが閉じられるとm_Validがfalseとなり、メインウィンドウ側は処理を中止すべき状態になったことを知ることができる。UpdateStatusを呼び出すことでダイアログに文字列を表示できる。

f:id:donadonasan:20200505102718p:plain

実装したダイアログの外観

A dialog with independent User Interface thread (C ...

 

logicool M705 ワイヤレスレーザーマウスを分解・修理した

logicool M705 ワイヤレスレーザーマウスを使っているのだが、ホイールの回転が悪くなってきた。内部にホコリが溜まったせいだと思い、分解・修理したので手順をメモ。

なお、M705にはいくつかのモデルが存在し、それぞれ構造が異なる。今回分解したのは2018年頃に発売されていたモデルナンバーM-R0073である。

 

分解対象外見

f:id:donadonasan:20200418162228p:plain

logicool M705 M-R0073 上面

 

f:id:donadonasan:20200418162313p:plain

logicool M705 M-R0073 裏面

裏カバーの取り外し

ネジは合計5本。うち2本は前方のシールに、2本は電池ケースのシールに隠されているので、これらを剥がす必要がある。

f:id:donadonasan:20200418162819p:plain

M705 M-R0073の裏カバーを固定するネジの位置

今回のマウスでは特殊ネジ(トルクスネジ、おそらくT6)が使われていた。ただ、logicoolのマウスは同じモデルナンバーでもネジ形状が違うことがあるので(例:M-R0009。プラスネジだったり六角ボルトだったりする)、M-R0073の全てで特殊ネジが使われているかどうかは不明。

f:id:donadonasan:20200418163404p:plain

M705 M-R0073のネジ形状

ネジ5本を外すと、容易に裏カバーが外れる。

f:id:donadonasan:20200418164525p:plain

M705 M-R0073 内部

ホイールユニットの取り外し

ホイールユニットはピン一本で本体とつながっているので、これを抜くと外れる。外す際に、ホイールユニットの下に隠れているバネがどこかに飛んでいかないよう、本体の水平を維持しながらゆっくり外すこと。

f:id:donadonasan:20200418164835p:plain

ホール部を固定しているピンを外す

f:id:donadonasan:20200418164956p:plain

小さいバネ2個が行方不明にならないよう気をつけること

ホイールユニットを外したところ。裏を見ると、ホコリがみっちり詰まっている。グロ画像かもしれない。

f:id:donadonasan:20200418165137p:plain

M705のホイールユニットと固定ピン

f:id:donadonasan:20200418165219p:plain

ホイールユニット裏側。みっちり詰まったホコリが見える。

ホイールユニットの分解

ホイールユニットは概略以下のような順番で分解する。ネジは②を固定しているプラスネジ1個のみ。

f:id:donadonasan:20200418165711p:plain

ホイールユニットの分解順番(概略)

ホイールユニットは12個の部品からできている。なお、下図ではバネ1個が4番に固定されたままであり、2番を固定するプラスネジは写っていない。

f:id:donadonasan:20200418170306p:plain

M705 ホイールユニット部品一覧

分解作業は、ホイールを透明部品から外すのに苦労するが、ほかはスムーズに進むはずである。なお、5番の部品は4番を外すと自然に外れるので、どこかに落ちないよう気をつけること。また、向きを覚えておくこと。

ホイールユニットの組み立て

組み立ては手こずるので、詳細に説明する。

まず、ホイールを透明部品に組み付ける。

f:id:donadonasan:20200418170633p:plain

ホイールを透明部品に組み付ける

白い5番パーツ2個を透明部品に入れる。5番パーツは一端が平坦なものと尖っているものがあるが、平坦なものがマウス上方に、尖っているものが下方に位置する。また、それぞれギザギザを有するが、それが噛み合うようにセットする。

f:id:donadonasan:20200418171239p:plain

5番パーツの装着。写真表面側が尖ったパーツ。写真裏面側が平坦なパーツ。

バネの付いた4番パーツを取り付ける。バネの固定位置は下図を参照。

f:id:donadonasan:20200418171657p:plain

4番パーツのバネ固定位置

3番の金具および3'のバネを取り付ける。向きは下図を参照。

f:id:donadonasan:20200418171843p:plain

3番の金具および3'のバネの取り付け

3'のバネを3番の金具に固定する。

f:id:donadonasan:20200418172002p:plain

3'のバネを3番の金具に固定する

2番のパーツをプラスネジで固定する。2番のパーツはこのネジ以外にも、1箇所スナップロックがある。

f:id:donadonasan:20200418172141p:plain

2番のパーツを固定。写真左側に1箇所スナップロックがある。

f:id:donadonasan:20200418172420p:plain

スナップロック位置

1番のパーツを取り付ける。向きに注意。

f:id:donadonasan:20200418172535p:plain

1番のパーツの取付向き

ホイールユニットを本体に取り付け、固定ピンを刺す。小さなバネがどこかに行かないよう注意すること。また、固定ピンは正しい向きで取り付けないとマウスの上面カバーが取り付けられない。下の写真では間違えてしまっている。コの字の開口部が後ろを向くようにすること。

f:id:donadonasan:20200418172810p:plain

ホイールユニットを取り付け、固定ピンを刺す。固定ピンの向きに注意。これは間違った向きの例。

あとはマウス上面パーツを取り付け、ネジを締めてシールを貼るだけである。上面パーツと裏カバーの間に隙間が開く場合は、固定ピンの向きが間違っている可能性があるので確認すること。

Ryzen3700Xに大手裏剣 参を装着してみた(ベンチマーク編)

Ryzen3700Xに大手裏剣 参を装着してみた(装着手順編) - ドナドナされるプログラマのメモ の続き。

Wraith Prismから換装する前と後で、色々とどう変わったか調べてみた。なにしろ、サイズがちっこいので冷却能力が足りるか不安なので。

冷却性能のベンチマーク

ベンチマーク内容:CPUの負荷およびPrecision Boost Overdriveの有無により、ファン回転数とCPU温度がどう変わるかを調べる。CPUの負荷はBOINCを使って100%としている。

 表 ベンチマーク結果

  Default Precision Boost Overdrive
  負荷0~2% 負荷100% 負荷0~2% 負荷100%
  Wraith
Prism
大手裏剣
Wraith
Prism
大手裏剣
Wraith
Prism
大手裏剣
Wraith
Prism
大手裏剣
最大クロック
(MHz)
(655) (896) 3781 3780 (707) (784) 3942 3902
CPU温度
(℃)
42.62 39.4 81.38 81.45 44.22 43.82 95 95.01
IC全体の発熱
PPT (W)
29.04 31.68 88 88 28.8 35.32 113.6 109.3
ファン回転数
(RPM)
2170 1173 2947 1713 取得し忘れ 1200 2934 1708
Precision Boost Overdriveを有効にし、負荷を100%としたときのPPTに若干の違いが出た。懸念したとおり、WraithPrismと比べるとわずかに冷却能力が低いようだ。そのため、大手裏剣 参はCPUの定格を超えて使う人には向かないと思われる。一方、定格で使う分には全く問題なく、低いファン回転数による静かな環境を構築できる。

音のベンチマーク


CPU冷却ファンの音を比べてみた:WraithPrism VS 大手裏剣参

関連記事:

Ryzen3700Xに大手裏剣 参を装着してみた(装着手順編) - ドナドナされるプログラマのメモ

 

 

 

Ryzen3700Xに大手裏剣 参を装着してみた(装着手順編)

Ryzen 3700Xに標準でついてきたWraith Prismは結構音が大きいので、CPU冷却ファンを買い替えてみた。ちょっと冷却能力に不安があったけど買ってみたのは大手裏剣 参。

www.scythe.co.jp

 

取付対象

寸法情報

f:id:donadonasan:20200412222355p:plain

大手裏剣 参 各部寸法

箱に、寸法情報が書かれていた。細かい寸法は、各自で画像から割り出してください。

Wraith Prism使用時のベンチマーク詳しくは別ページで。

f:id:donadonasan:20200412224621p:plain

CPU使用率がほぼ0の場合の消費電力と温度、ファン回転数

Wraith Prismと大手裏剣 参の大きさ比較。

f:id:donadonasan:20200413001104p:plain

Wraith Prismと大手裏剣 参の大きさ比較

大手裏剣、ファンのサイズは大きいけど放熱部の寸法はかなり小さい。自分のように大きなPCケースを使っていてスペースに余裕があるのなら、無限五 リビジョンBなどを使ったほうがいいかもしれない。(若干後悔中)

www.scythe.co.jp

換装手順

CPUファンの換装手順を示します。部品の向き等は付属の説明書を確認してください。

ちなみに、換装前のPC内部はこんな感じ。

f:id:donadonasan:20200412231449p:plain

Wraith Prism使用時のPC内部(換装前)

1. Wraith Prismのレバーを反対側に倒し、固定を解除する。

f:id:donadonasan:20200412234913p:plain

Wraith Prismの固定解除

2. プラスチックの爪から金具を外す(合計2箇所)。

f:id:donadonasan:20200412235226p:plain

Wraith Prismの固定金具を外す

3. CPUとファンはグリスにより固着しているので、Wraith Prismが動くまでグリグリと左右にねじって固着を解除する。動きそうもない場合は、PCの電源を入れて温め、グリスを柔らかくする。

4. ファンを外す。

f:id:donadonasan:20200412235726p:plain

CPUファンを外したところ

5. マザーボードについているプラスチックの部品のうち片方を外す。マニュアルでは両方外すことになっているが、そうしてしまうとマザーボードの裏にあるバックプレートが外れてしまう。それが嫌だったため、以下ではプラ部品を片方ずつ外すことでバックプレートを外さずに作業する。

f:id:donadonasan:20200413001933p:plain

Wraith Prismを保持していたプラスチック部品の片方を外す。プラスドライバー必須。

6. 大手裏剣 参に付属するプラスチック部品を2個はめる(ここはマニュアル通り)。そして、バックプレートを固定するため軽くネジで締める(ここはマニュアルと異なる)。ネジがゴム部にめり込まないようにすること。使うネジは、付属のもののうち短い方。

f:id:donadonasan:20200413003053p:plain

プラ部品の固定とネジ締め

f:id:donadonasan:20200413002658p:plain

AM4ソケット用に使うネジ

6. 残ったWraith Prism固定用のプラ部品を外す。

f:id:donadonasan:20200413003417p:plain

Wraith Prism用の固定部品を外す(2個目)

7. プラ部品を固定し、片方だけ軽くネジを締める

f:id:donadonasan:20200413003949p:plain

プラ部品2個を取り付け、片方だけ軽くネジ締めする

8. プレート固定準備のため、ネジを一個外す。

f:id:donadonasan:20200413004150p:plain

ネジを一個外す

9. 大手裏剣 参固定用のプレートをしっかり固定する。プレートには上下があるので注意。マニュアル参照。

f:id:donadonasan:20200413004622p:plain

大手裏剣 参固定用のプレートを固定する

10. 反対側のプレートも固定する。

f:id:donadonasan:20200413004833p:plain

反対側のプレートも固定する

11. 大手裏剣 参の底面についているシールを剥がす

f:id:donadonasan:20200413005046p:plain

保護シールを剥がす

12. 熱伝導グリスを塗布する。CPUと冷却ファンの間が埋まる、必要最低限の量にするのが良いのだが・・・どのくらいが必要最低限なのかわからない。とりあえず適当に。

f:id:donadonasan:20200413005405p:plain

熱伝導グリスを適当量おいてみた

f:id:donadonasan:20200413005454p:plain

熱伝導グリス塗布後

13. 大手裏剣 参の取り付け。ネジは左右交互に少しずつ締めること。詳しくはマニュアル参照。なお、冷却ファンは張り出し部がメモリの上にくる向きと、M.2 SSDの上にくる向きの2通りの向きに取り付けることができる。メモリの上に来る向きの場合、メモリの冷却も狙えるけどメモリが外せなくなる。M.2 SSDの上に来る場合はメモリの取り付け・取り外しは問題なくできそう。M.2 SSDの取り外しもたぶんできるので、そちらのほうがおすすめかも知れない。自分はメモリ増設の予定がCPU交換まで無いので、前者を選択。

f:id:donadonasan:20200413005712p:plain

大手裏剣 参の固定

別の向きでの取り付けイメージを参考までに。

f:id:donadonasan:20200413010903p:plain

大手裏剣 参を別の向きで固定した場合のイメージ

14. ファンの電源をマザーボードに固定する。

取り付け状況

取り付け結果。M.2 SSDとはまったく干渉していない。PCI Expressコネクタとも十分距離がある。

f:id:donadonasan:20200413011044p:plain

大手裏剣 参の取り付け結果

メモリとの干渉状況。使用メモリは冷却プレート付きの大きなものだが、当たらずに済んでいる。

f:id:donadonasan:20200413010534p:plain

メモリと冷却ファンの干渉状況

PCの内部状況。エアフローも問題なさそう。

f:id:donadonasan:20200413011152p:plain

大手裏剣 参取り付け後のPC内部