AKAGI Rails

鉄道模型シミュレーターで遊んでいたはずが、気づいたらPythonなども。

vrmapi.LOG()に文句タラタラ

VRMNXでログ出力に使うvrmapi.LOG()ですが,いや,実はコレが文句タラタラでして・・・。

  • 型がstr, int, floatしか受け付けてくれない
  • したがってうっかりdictやlistを入れるとTypeErrorが出てくる
  • listやdictを使うシーンでのデバッグではstr()を噛まさないといけないけど面倒くさいし,忘れる
  • pythonオブジェクトのstr属性のことを何だと思っているんだ!

こういうものは,仕方がないので自分で何とかする。

def LOG(*objects, sep=' ', end=''):
    """すこし賢いログ出力。
    
    Python標準のprint()とほぼ同じ形式でVRMNXのスクリプトLOGに出力します。
    objectsのすべてにstr()を引っかけて,sepで区切りながらつなげて出力します。
    スクリプトLOGではString型と表示されますがobjectsの実体とは無関係です。
    sep, endを設定する場合キーワード引数で設定してください。
    
    Args:
        objects: LOGに出したいオブジェクト。str()した上で出力します。
        sep: objectどうしの区切り。
        end: 行末。
    """
    output = sep.join(map(str, objects)) + end
    vrmapi.LOG(output)

文句タラタラだけど,旧世代のVRMスクリプトよりは5000兆倍素晴らしいのでがんばってやっています。

でも実は,地味にすごい(?)機能もログウィンドウには付いています。

f:id:AKAGI-vrmstation:20200503141550p:plain
エラーを出したコードが書いてあるオブジェクトがどれだか分かる

エラーロガーとしては当たり前の機能なような気もしますが,VRMNXのPythonエンジンはどのオブジェクトに書いたコードも区別なく実行する…ように見えていたのに,VRMNX側(vrmapiの実体)には区別できるらしいです。

Advanced Trackのポイントのまくらぎが気になったので無理矢理直した

Advanced Trackは高精細な作りでレイアウトの見た目をぐっと引き締めてくれますが,見た目上どうしても気になる点があります。

それは, PCまくらぎが使われている 点です。

f:id:AKAGI-vrmstation:20200427202353j:plain
本来はポイントにPCまくらぎは使わない

PCまくらぎというのは,ネジを締め込むための穴を予め決まった場所に開けてあるので,ポイントのようにいろんな場所にクギを打ち込む場所には使わないみたいです。ということで,ポイントの部分だけはいまだに木まくらぎが使われている場所が多いです。

f:id:AKAGI-vrmstation:20200427203818j:plain
スラブポイントは合成まくらぎみたいなのが付いている

Advanced Trackはリアルな作りがウリなのに,まくらぎだけイマイチなので残念です。

そこで,「線路付帯設備」に入っている,単品のまくらぎを重ねて並べてみました。

f:id:AKAGI-vrmstation:20200427202350j:plain
ついでに転轍機も

線路に対して+3.6mmで,4mm間隔で並べると,ちょうどPCまくらぎの模様に重なります。(ただし,分岐側のまくらぎ模様が斜めになっている部分は下が見えてしまいます。ボルトの位置も合わない…w)

f:id:AKAGI-vrmstation:20200427204635p:plain
1936ポイント1基に104本の枕木(木製)が並べてある

左分岐を作って力尽きました。右は今は作りたくありませんw

Factorioのサプライチェーンネットワーク

Factorioの各アイテムについて、材料として何が必要かの関係性をとりあえず図示してみました。

  1. 各アイテムの材料をExcelにデータベースのように打ち込む(手打ちしたw)
  2. NetworkXというライブラリで、アイテムをノード、依存関係をアークとするようなグラフをExcelを元に作る
  3. NetworkX (というかmatplotlib.pyplot) で可視化

f:id:AKAGI-vrmstation:20200421235538p:plain
必要なモノ同士が矢印で結ばれている

ゲームの進み具合的に、まだ宇宙サイエンスパック(白フラスコ)を製造する必要がなさそうなので、それ以外のフラスコの製造に必要なアイテムのみ抽出してグラフにしてあります。

各ノードの位置関係についてはまだしっかりと作り込んでいませんが、このグラフに表された関係性が、各アイテムの必要数の解析や、施設配置最適化を考えていくための土台となります。

Factorioに血が騒ぐ

Factorioというゲームは前から知っていましたがなかなかやらずにいました。

f:id:AKAGI-vrmstation:20200415235925p:plain

ところがですね、やってみると意外にこれが面白い!そろそろ石油工場がオープンするあたりまで進んできました。

store.steampowered.com

工場(といっても、広大な大地に直接いろんな設備を置く)を作っていろいろなものを生産していくゲームです。燃料を採掘し、鉄鉱石を掘り、様々な加工・組立のプロセスを繰り返していきます。

ゲームをすすめていくと物流手段に「鉄道」が使えるようになるのもハマる要素の一つですが、 実は「経営工学」を専攻していたので、IE(インダストリアル・エンジニアリング)には一家言あるわけですワ~(ゲームをするのはヘタクソですが)。

各工程を担当させる加工機械をどのように配置するか、は工場内レイアウトを考えていることになります。そのためのIE的手法に、SLP (Systematic Layout Planning) というものがあります。

SLPをする上では工程(施設)どうしの関連性を調査する必要もありますが、そのためにはアローダイアグラムが使えます。アローダイアグラムは実際の工場の工程編成を決めるのにも使われていたはずです。

各工程では、「必要なものが 必要なときに 必要なだけ」届く必要があります。このくらいはJITとかカンバンとかいろいろな言葉で、社会的にも断片的に認知されているはずです。カンバン方式などプル型の生産システムではは、後工程から前工程に注文を伝播させていく形式で、下流工程が上流工程のことをあまり気にしなくてもよいように工夫されています。(が、各ポイントでのカンバンの枚数を決めるのはけっこう大変です)

しかしFactorioでは注文の概念が扱いづらい(回路部品というのを使えばできるかも、と思ったけど、できるのか?)ので、前工程から後工程にものを押し出していくプッシュ型の生産システムを構築するのが簡単そうです。ところが、製品Xと製品Yは共通の部品Pとか共通の原料Aを持っていたりなんかして、「何がどれだけ必要か」は上流にいくほど分かりにくくなります。これを勘定する手段にはBOM(部品展開表)というのがあります。どうやら欧州の自動車製造はBOMで回っているらしいです。

アローダイアグラムを使った分析手法のひとつにPERTがあります。本当は大量生産ではなくプロジェクトマネジメント(一回きりのものごと)の日程計画に使われるツールですが、計算をちょっと応用すると、加工機械の最適な設置台数の検討やボトルネック工程の予測がきくようになります。

ということで、BOM(もどき)とPERT(もどき)を組み合わせて、すてきな工場を作りたいなあと思っています。

VRMNXの時間系イベントが使いにくいなあと思っているアナタへ

実はそんな人いない説

はともかく。

まずはバージョン5以前の時間系イベントとVRMNXpyの時間系イベントの仕様の相違について,Afterイベントを例に見てみましょう。

バージョン5以前のイベントは,

//イベントを設定
SetEventAfter Target Method EventID Interval
// Target: 対象オブジェクト
// Method: 対象オブジェクトのメソッド
// EventID: イベントIDを受け取るグローバル変数(のポインタ)
// Interval: 時間間隔(ms)

BeginFunc Method
    // Intervalミリ秒後に実行する制御の中身
EndFunc

という仕様でした。一方でVRMNXpyでは

#イベントを設定
evid = target.SetEventAfter(Interval)
# target: 対象オブジェクト
# interval: 時間間隔(s)
# (返り値) evid: イベントID

#指定時間経過すると対象オブジェクトのイベントハンドラが呼び出される
def vrmevent_xx(obj,ev,param):
    if ev=='after':
        # Afterイベントが起きたときの制御の中身

聡明な読者諸兄には自明なことかと思いますが,VRMNXpyのイベントは,どのイベントIDであってもとりあえず同じコードを走らせてしまうという仕様になっています。イベントハンドラの中に様々な処理をベタ書きしているといつかバグの温床となるでしょう。

この問題を解決するひとつのアイデアとして,次のような実装を提案することができます。ただし,一部が理解のしやすさのため多少冗長な書き方になっています。

def SetEventAfterAKG(target, callback, interval):
    """ 実行対象の関数を明示するAfterイベントのラッパ
    * vrmapiのイベントハンドラに専用コードを仕込む必要がある *
    Parameters -----
    target: 対象オブジェクト
    callback: このAfterイベントで実行されるコールバック関数
    interval: 時間間隔(秒)
    Returns -----
    eventid

    callback関数はobj, ev, paramの3つのparameterを伴って呼び出されます。
    """
    # vrmapiにイベントを登録
    evid = target.SetEventAfter(interval)
    # 対象オブジェクトのdictにコールバック関数を登録
    d = target.GetDict()
    evkey = 'AKGCB{}'.format(evid)
    d[evkey] = callback
    return evid

# 今回Afterイベントで仕込みたい制御がこちらだとしましょう
def sample_cb(obj,ev,param):
    # 制御の中身(省略)
    pass

# イベントを設定する際のコード
evid = SetEventAfterAKG(target, sample_cb, interval=3)

# 対象オブジェクトのイベントハンドラの中身で・・・
def vrmevent_xx(obj,ev,param):
    if ev=='after':
        # 以下が専用コード
        d = obj.GetDict()
        evkey = 'AKGCB{}'.format(param['eventid'])
        try:
            callback = d[evkey]
        except KeyError:
            # ラッパ経由で登録しなかったAfterイベントにはDictにcallbackが入っていないのでエラーが送出される
            # お行儀は最悪だけどとりあえずシカトする
            pass
        else:
            # Dictからcallbackがエラーなく見つかったときだけコールバックを呼び出し
            callback(obj,ev,param)

これで一応,バージョン5互換(もどき)にAfterイベントのcallback処理を作り込んでいくことができます。イベントハンドラ(vrmevent_xx)の中に処理をベタ書きせず,外へ独立した書き方ができるのでいろいろ楽ちんであると思います。見た目的には,インデントを浅くする効果があり全体がPythonらしいスッキリとした仕上がりになるはずです。 実行時のことを考えると,ほどよく名前空間を切ることになるので不要なオブジェクトのメモリが早く開放されるのではないかと思います。Pythonの関数呼び出しは結構重たいことが知られています(注:リンク先のサンプルコードはPython2仕様である。)がframeイベントで何百個も関数を呼び出すとかでなければ大丈夫でしょう。

target.SetEventAfterAKG(...)ではなく,SetEventAfterAKG(target, ...)でイベントを登録するお作法に混乱するかもしれませんが,これはSetEventAfterAKGがvrmapiのSetEventAfterのように各オブジェクトのメソッドとしてでなく,グローバルな関数として定義することを想定しているからです。

このアプローチであればVRMNXpyにKillEventが存在しない問題も,割と簡単に解決します。KillEventのかわりに,Dictから当該のイベントのcallbackを保存しているメンバを取り除いてしまえばよいのです。

d = obj.GetDict()
d.pop('AKGCB{}'.format(evid_to_kill))

札幌取材

自作車両の第2弾にむけて,札幌圏で旅行を兼ねて取材をしてきました。

今回は,形式図やネットの写真で施工可能な場所をできるだけ進めたうえで,よくわからない部分を調べに行くという感じでした。

遠方なので,できる限り効率的に。あらかじめGoogleの航空写真などで屋上や左右サイドを撮れそうなポイントを把握しておき,数が少ないキハ201系の運用と突き合わせて撮影ポイントを転々とします。止まっていてくれた方がさまざまな角度でゆっくり撮れるので駅が中心ですが陸橋なども重要です。

Sm_dsc6411 Sm_dsc6377 Sm_dsc6451 Sm_dsc6462

床下機器や屋上機器がよく見える写真はネット上では得にくいので,自分の足で撮りに行くしかありません。

続きを読む

自作車両PJ第2弾 スタート

107系に続き,VRM自作車両の第2弾に着手しました。電車&気動車のセットで仕上げるつもりです。

190828side

107系よりもかなり造形が複雑です。かなりいいデザインなのですが,衝撃に強いという機能的にも優れているらしいです。