Factorioでヒステリシスつきエッジカウンタ。
入力信号がリセット状態からセット状態になると1パルスだけ信号を出力します。
以下、BluePrint.
今まで使っていたThinkPad X240のキーボードが不具合をおこした。修理しようかと思ったけどかなり古いPCでもあったので、買い替えることに。そこで、何を買うか検討してみた・・・が、比較表を作っただけで時間切れ。なんで各社とも、発表内容と仕様が食い違うなどのミスが多いのだ。ノートPC業界はいい加減なんだろうか?
4年間使用、毎月¥3,000相当の価値があるとして¥144,000程度まで
ThinkPad L14 Gen 1(AMD) | 豊富な拡張性と接続性を備えた14型メインストリームPC | レノボジャパン
ThinkPad L15 Gen 1 (AMD) | 豊富な拡張性と接続性を備えた15.6型メインストリームPC | レノボジャパン
ThinkPad X13 Gen 1 (AMD)| コンパクトでパワフルな13.3型モバイルノート | レノボジャパン
ThinkPad T14s Gen 1 (AMD) | 14型ハイパフォーマンス・スリム・ノート | レノボジャパン
ThinkPad T14 Gen 1 (AMD) | 14型ハイパフォーマンス・ノート | レノボジャパン
Lenovo Legion 550 | AMD Ryzen搭載15.6型ゲーミングノート | レノボジャパン
Dell G5 15インチ ゲーミング ノートパソコン(AMD Radeon CPU搭載) | Dell 日本
ASUS TUF Gaming A15 | ASUS TUF Gaming | ノートパソコン | ASUS日本
ROG Zephyrus G14 | Gaming | ノートパソコン | ASUS日本
FactorioのSpace Exploration Modをプレイしていると、そのうち母星の資源が枯渇してくる。そうなると他の惑星へ資源を奪いに行く必要がある。Factorioの醍醐味は様々な工夫による作業の自動化にあるので、できれば奪った資源の輸送も自動化したい。しかし、ここで問題になるのは貨物ロケットの発射準備方法。貨物ロケットを発射するには、発射台に以下をセットする必要がある。
このうち、液体燃料は生産・貯蔵設備からパイプを発射台に繋げておけば勝手に必要量だけ重点されるので楽なのだが、残りの2つが曲者。発射台にはロケットに載せる貨物もセットできるのだが、貨物のセット先も上記ロケットの部品のセット先も共通なのだ。そのため、何も考えずにインサーターで宇宙カプセルや貨物ロケットセクションを発射台にセットする構成にすると、必要量以上がセットされてしまい貨物ロケットがこれら部品で埋まってしまう。そのため、以下の仕掛けが必要になる。
発射準備中かどうかは発射台から出てくる信号で識別ができ、0が準備中、1が準備完了である。またFactorioでは信号の比較、信号の加減乗除ができ、ラッチ回路等も作れる。以上の道具立てからすると、実現方法は・・いくつもあるなあ。思いついた各方式の概要を列挙してみる。
・・・あれ?ラッチとかが必須かと思ったけど、一番最後のをちょっぴり改良して、セットするインサーターを発射台からの信号で直接制御してあげれば行けるんじゃない?つまり、こうだ。
でも、これは失敗。セットする方にスタックフィルタインサータを使ったにも関わらず、なぜか普通のフィルタインサータが全てを瞬時に除去してしまうのだ。
ということで、他の方式を検討する。まずラッチ方式だが、リセット信号のエッジ検出がめんどくさそうなのでパス。そこで、信号が1の間は部品をセットせず、信号が0の間だけ所定の個数セットする方式にする。所定の個数をカウントする回路のクリアは、信号=1を条件とすればタイミングチャート的に大丈夫なはず・・。
などと思いながら回路を組んでいたら、発射台からセット済みアイテムの個数も出力されていたことが発覚。ラッキー。(でも考えたり記事書いたりしたのにかかった数時間は無駄に)
Factorioでアイテム生産に必要な工場の数を試算するためのツール, FactorioFactoryCalculatorを公開しました。これを使えば、例えば製造サイエンスパックを毎秒3個作るために必要な、生産力モジュール1の工場の数やそのための発展基板工場の数などが試算できます。
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する。
状態を表示するためのエディットボックスと処理を中止するキャンセルボタンを有するダイアログの実装例を以下に示す。この例では、以下のような構成となっている。
A dialog with independent User Interface thread (C ...