AKAGI Rails

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

踏切のグループを拡張しよう

今回は,Pythonの「クラス」を使ってみます。

VRMスクリプトにもあった CrossingGroupCTRLVRMNXpy にも実装が引き継がれましたが,これで踏切を作動させても踏切警報機にある方向表示機は真っ暗なままです。接近する列車の方向や何本もの列車が同時に踏切に接近してくる場合も考慮するとなれば,やや複雑な処理が必要です。

Crossing


同じようなことを実現する自動踏切スクロールを過去に作りましたが,これをVRMNXpyで実装してみましょう。ただし,今回は遮断桿が遅れて降りてくるのは省略です。(次回やります)

クラスとかインスタンスという言葉が聞き慣れない方は, Pythonで学ぶ基礎からのプログラミング入門~オブジェクト指向について学ぼう が分かりやすいです。(8本とやや長めです)

<注意: VRM NXは開発途上の段階であり,βテストの経過に伴って仕様変更の可能性がある。本エントリに示す情報は執筆時点(v6.0.0.25)でのものである。> (2019/1/17 現在 Ver. 6.0.0.25)

拙作自動踏切スクロールや,その先行事例であるghost氏の自動踏切スクロールは,踏切部品のうちのどれか一つを「制御の親」として,取り巻きの踏切部品の制御の中核となるコードを持たせていました。

VRMNXではPythonらしい方法を採りたいと思います。CrossingCtrlerというクラスを新たに定義し,「制御の親」の役割を持たせます。CrossingCtrlerオブジェクトは,レイアウター上の特定の部品と1対1に対応するわけではなく,仮想的なものと言えます。1つの踏切を構成するいくつかの踏切部品をどれも対等に従わせて,動作をコントロールします。

CrossingCtrlerの役割は次のような感じです。

  • 自分の管轄する踏切部品(警報機と遮断機)のリストを持っている。
  • (方向表示機を線路の両側で正しく動作させるために)踏切警報機が基準に対して順方向か逆方向かを知っている。
  • センサーから列車の接近・通過を知らされると,
  • 通過方向ごとに接近中の列車本数を管理して(複々線対応のためでもある)
  • 然るべく踏切部品を動作させる

管理すべき踏切部品のリストや接近中の列車本数はCrossingCtrlerオブジェクトのインスタンス変数として持たせるのにぴったりです。列車の接近・通過のときに呼び出すメソッドをこしらえておいて,センサーから呼び出すことにしましょう。

では,新たにCrossingCtrlerクラスを定義してみましょう。レイアウトスクリプトにただ1回だけ書けばOK。

class CrossingCtrler:
    def __init__(self, name):
        self.name = name      # 踏切の名前を好きに設定できるが特に使わない
        self.components = []  # タプル(踏切オブジェクト, True(逆方向のとき)/False(順方向のとき)
        self.train = {1: 0, 2:0}  #接近中の列車本数(方向ごと)

    def add_component(self, obj, rev):
        # 踏切部品をCrossingCtrlerに登録するメソッド
        # obj: 踏切オブジェクト, rev: 方向表示機を逆転するときTrue, さもなくばFalse
        self.components.append((obj, rev))

    def train_approach(self, dir):
        # 列車接近時に呼び出す。dir=通過方向(1 or 2)
        if dir in [1, 2]:
            self.train[dir] += 1
            self.update()

    def train_pass(self, dir): 
        # 列車通過時に呼び出す。dir=通過方向(1 or 2)
        if dir in [1, 2]:
            self.train[dir] -= 1
            self.update()

    def update(self):
        # 踏切部品の動作部分をまとめた関数
        sign = 0b0   # 方向表示機の表示状態を作る変数(2進表記)
        if self.train[1] > 0:
            sign = sign | 0b1  # AND演算
        if self.train[2] > 0:
            sign = sign | 0b10
        if sign:
            # 接近列車のあるとき
            for obj, rev in self.components:
                if rev and sign < 3:     # 逆向きの警報機は,片方向のみ接近中のときに限り,
                    sign = sign ^ 0b11   # 方向表示を逆転(XOR演算)
                obj.SetCrossingStatus(2) # 踏切を閉じる
                obj.SetCrossingSign(sign)
        else:
            # 接近列車のないとき
            for obj, rev in self.components:
                obj.SetCrossingStatus(1) # 踏切を開く
                obj.SetCrossingSign(sign)

CrossingCtrlerのインスタンスの生成と,踏切部品の登録は次のようにします。ここで,fumikiriはグローバル変数でなければならないので,イベントハンドラの外に書きます。レイアウトのGetCrossingメソッドから部品ID=83, 84の踏切オブジェクトを取得する書き方にも注目。

fumikiri = CrossingCtrler("猫踏切") # インスタンスを生成しfumikiriに保存
fumikiri.add_component(vrmapi.LAYOUT().GetCrossing(83), False) # 踏切部品を追加
fumikiri.add_component(vrmapi.LAYOUT().GetCrossing(84), True) # 最後のTrue:方向表示機を逆転

列車の接近や通過を知らせるセンサーのイベントハンドラから,fumikiriのtrain_approach (接近時), train_pass (通過時) を呼び出すといい感じに踏切が動きます。方向表示機動作用の列車通過方向を引数に与えます。

fumikiri.train_approach(2) # 方向2で列車接近
fumikiri.train_pass(2) # 方向2で列車が通過

ちなみに,方向1とか方向2とかは,踏切部品のSetCrossingSignのいう方向と同じで,I.MAGICの踏切部品では図のようになっています。

Cros_sign

説明だけでは納得しづらいと思いますので実際に動くサンプルレイアウトもアップします。ただし,レイアウトの動作およびOSやコンピュータなどに与える影響,その他の不都合もふくめ一切の責任を負いかねますのですべて自己責任のもとでお願いします。そして当然ながら踏切部品(VRM4第4号互換パッケージにも収録)とそれをアンロックするパッケージが必要です。

「nx_fumikiri.zip」をダウンロード(1.15MB)

遮断機の時間差作動は,今回再現を見送ってしまいましたが,マルチスレッドが利用できれば,vrmapiの提供するタイマーイベントを使わずに済み,設定に必要なコードのコピペの手間を減らせます。現時点では,Python付属のLIBの使用については、VRMNXではサポートしていません。importしても問題ないか確認いないため、サポート外としてご利用ください。との事情も考慮して,冒険はまたの別稿で。