SSブログ

センサーの状態を読み込む [python]

これでほぼ完成なんですが、あと1点、発車メロディーがなっているときに列車を止めておきたいので、特定のセンサーがアクティブなときにはスピードを0にしておきたいと思いました。
流儀に大分慣れてきましたので、こちらは簡単でした。
センサーIS1の状態を読み出すには
sensors.getSensor('IS1').state
です。
ここには数値が入ります。
print sensors.getSensor('IS1').state

>>> print sensors.getSensor('IS1').state
2
>>> print sensors.getSensor('IS1').state
4
などと返ってきます。2がactive、4がinactiveです。1はunknownですので、エラー時の対処が可能です。
なので、
_________if sensors.getSensor(sensList[i]).state == 2:
____________self.sp = 0
などとしてやります。
例によってsensListに見るべきセンサー(発車メロディーを管理しているセンサー)のリストを作っておきます。

sensList = ['IS25', 'IS25', 'IS133', 'IS134', 'IS25', 'IS25', 'IS25', 'IS25', 'IS132', 'IS131', 'IS25', 'IS25', 'IS25', 'IS130', 'IS129', 'IS25', 'IS25', 'IS25', 'IS136', 'IS135', 'IS25', 'IS25', 'IS25', 'IS25']
IS25は発車メロディーのない一般の閉塞、場内信号機用のダミーですが、こいつをactiveにすれば全列車(出発信号機に支配されているものは除く)を止めることが出来るセンサーになります。

これ追加してversion 1が完成です。

タグ:DCC Python

現示変化を待つ [python]

信号現示が変化したときだけの動作には通常はリスナーを使うのでしょうか。
しかし当時はサンプルスクリプトの構文を見ていてもよく理解できませんでしたので、
別の方法を探してみました。

JMRI APIにちゃんと用意されています。
self.waitChange(jarray.array(self.c,jmri.NamedBean))
というやつで、ここのself.cに信号機のリストを代入してやると、その中のいずれかに変化があるまで、waitするというものです。まさにぴったり。

ところが
self.c = ['IH:SE8C:"LT273";"LT274"', …….]
などとしてやっても、全く動きません。なんで。。。

またしても、ネットで検索、なるほどsignals.getSignalHead()で読み込んだコードでないといけないようです。printしてみたところ、わけの分からないコードのようなものでした。
これは手で作成するわけにはいきません。(起動するたびに変わるかも???)

self.a = []
self.c = ['IH:SE8C:"LT273";"LT274"', ……]
for self.b in self.c:
___self.a.append(signals.getSignalHead(self.b))

1行目で空のリストself.aを作って、
self.bをself.cの信号リストに対し、回します。
ループの中はリストself.aにgetSignalHeadで読み込んだコードを追加していきます。
これで、
self.waitChange(jarray.array(self.c,jmri.NamedBean))
とすれば、どれかの信号現示が変わったときまで、プロセスが止まるようになりました。
分かってしまえば、、、なんですが、これはちゃんと動くようになるまで結構苦労しました。

複数の列車を制御 [python]

昨日ご紹介した列車制御を各列車に適応させるには昨日のループを
各列車に対してループjを回せば良いことになります。

for j in range(len(trainList)):
___for i in range(24):
______self.ichi = memories.getMemory(memoriList[i]).getValue()
______if self.ichi == trainList[j]:
_________if (i == 0) and (memories.getMemory(memoriList[23]).getValue() == trainList[j]):
____________i = 23
_________self.singo = singoList[i]
_________self.iro = signals.getSignalHead(self.singo).getAppearanceName()
_________break
___if self.singo == '':
______self.iro = 'Red'
___if self.iro == 'Green':
______self.sp = 1
___elif self.iro == 'Yellow':
______self.sp = 0.5
___elif self.iro == 'Red':
______self.sp = 0
___print trainList[j], i, self.singo, self.iro, sensors.getSensor(sensList[i]).state, self.sp
___self.throttle = self.getThrottle(int(trainList[j]), False)
___self.throttle.speedSetting = self.sp
___self.singo = ''

trainList = ['7', '11', '31', '21', '22']

新しい車両が入線したら、trainListにDCCアドレスを追加すればOKです。
このルーチンが一周すれば、全列車に対して1回、前方の信号現示を確認して、スピードを調整することになります。
ですので、クラスの定義のところにあった
def handle(self):
___……
___return 1
の___……にさっきのループを書けば、クラスを実行したときに無限に回り続けます。
ただ、これでやるとThread monitorで見るとものすごい速さで回り続けていることが分かります。
全列車止まっていても、意味も無く回っています。マシンパワーが。。。。
ですので、このループを回すのは信号が変わったときだけで良いのです。
さて、どうしましょうか。

特定のDCCアドレスの列車を探す [python]

次に列車を探すルーチンを考えます。
ある変数に特定の列車の位置をトレース次々と代入していく方法があると思いますが、私の拙い頭では簡単なアルゴリズムが思いつかなかったので、列車を探しに行くようなアルゴリズムにしました。
BASICのfor nextループと同じような感じです。

for i in range(24):
___self.ichi = memories.getMemory(memoriList[i]).getValue()
___if self.ichi == "7":
______if (i == 0) and (memories.getMemory(memoriList[23]).getValue() == "7"):
_________i = 23
______self.singo = singoList[i]
______self.iro = signals.getSignalHead(self.singo).getAppearanceName()
______break

pythonでは左インデントでループの範囲を規定しますが、ここのページでは先頭のスペースは切り詰められるので、ここでは______で示しています。
iを0から23まで回します。24個の閉塞ブロックに渡ってメモリー値をスキャンしていって、途中で値“7”が出れば(最初のif のところ)、信号現示を読みに行き、breakでループを抜けます。
#DCCアドレス7の列車を探しに行くときの例です。

2つめのif のところ、i == 0のときだけ一番後を見に行き、そちらの値も7ならばi = 23とします。
メモリーリストは列車の進行方向とは逆向きに列べてあり、若いリストから見ていき、最初に”7”が出たところに先頭がいるはずです。これはうちのレイアウトが簡単なエンドレスだから出来ることだと思います。こうした理由は、ブロックをまたがって列車がいるときには’7’の値を持つメモリーが2つあり、後側の値を採用してしまうと信号現示が赤となるのを防ぐ手立てです。0番目でヒットしたときだけそのブロックの前は最後尾23ですので、それをifで見に行ってます。

信号現示を読み取れれば
if self.iro == 'Green':
self.sp = 1
elif self.iro == 'Yellow':
self.sp = 0.5
elif self.iro == 'Red':
self.sp = 0
self.throttle = self.getThrottle(7, False)
self.throttle.speedSetting = self.sp

とベタに書いて、列車を減速・停止させることが出来ます。
これを複数の列車に適応させていきます。

メモリーと信号のリスト [python]

さて、前回までにメモリーの読み出し、信号現示の読み出し、スロットルの制御が出来ることが分かりましたので、
特定の列車の位置を探し出し、信号現示に従って速度を制御するスクリプトを試作していきます。

まず、読み出すべきメモリー値や信号のSystem Nameを登録します。
pythonではリストとして変数を持てます。
例えば、メモリーのリストを
memoriList = ['IM:AUTO:0005', 'IM:AUTO:0004', 'IM:AUTO:0020', 'IM:AUTO:0019', 'IM:AUTO:0018', 'IM:AUTO:0014', 'IM:AUTO:0013', 'IM:AUTO:0012', 'IM:AUTO:0021', 'IM:AUTO:0022', 'IM:AUTO:0011', 'IM:AUTO:0010', 'IM:AUTO:0009', 'IM:AUTO:0023', 'IM:AUTO:0024', 'IM:AUTO:0001', 'IM:AUTO:0002', 'IM:AUTO:0003', 'IM:AUTO:0015', 'IM:AUTO:0016', 'IM:AUTO:0017', 'IM:AUTO:0008', 'IM:AUTO:0007', 'IM:AUTO:0006']
こんな感じで定義できます。

3つめを取り出すときは
a = memoriList[2]
です。(0からなので、3つめは2)

このメモリーリストに対して、参照すべき信号のリストを
singoList = ['IH:SE8C:"LT273";"LT274"', 'IH:SE8C:"LT275";"LT276"', 'IH:SE8C:"LT287";"LT288"', 'IH:SE8C:"LT285";"LT286"', 'IH:SE8C:"LT283";"LT284"', 'IH43', 'IH:SE8C:"LT261";"LT262"', 'IH:SE8C:"LT263";"LT264"', 'IH:SE8C:"LT301";"LT302"', 'IH:SE8C:"LT303";"LT304"', 'IH45', 'IH:SE8C:"LT265";"LT266"', 'IH:SE8C:"LT267";"LT268"', 'IH:SE8C:"LT305";"LT306"', 'IH:SE8C:"LT307";"LT308"', 'IH46', 'IH:SE8C:"LT257";"LT258"', 'IH:SE8C:"LT259";"LT260"', 'IH:SE8C:"LT277";"LT278"', 'IH:SE8C:"LT279";"LT280"', 'IH:SE8C:"LT281";"LT282"', 'IH44', 'IH:SE8C:"LT269";"LT270"', 'IH:SE8C:"LT271";"LT272"', 'IH47']
このように決めます。
メモリーは占有センサ(=閉塞区間)のブロックに対応してます。リストの同じ順番にその閉塞区間で見るべき先の信号機を対応させています。
IH44など、ダミーの信号機の部分は場内信号機で、場内の場合はどれか一つが緑現示なら緑、黄なら黄となるようにダミーを立てています。そのようなLogixを組んでいます。

たとえば、memoriList[10]の列車はsingoList[10]を参照すればいいようになってます。
ですので、ある列車がmemoriList[10]にいることが分かれば、singoList[10]の現示に従って速度を調節すれば良い訳です。

次回は、目的の列車がどこにいるかを探すルーチンです。
タグ:Python DCC

信号現示を読み取る [python]

次は信号現示を読み取ります。

信号現示を読み取るのも同じように
a = 'IH:SE8C:"LT273";"LT274"'
b = signals.getSignalHead(a).getAppearanceName()
で読み出せます。
もちろん変数を使わずそのまま引数に書いても大丈夫です。

ここも信号機はSystem Nameですので少々やっかいですが、定義してしまえばおしまいなので構わす進みます。
print bとすると、
Red
などと出力されます。

なかなか順調。
「メモリー値から列車の場所を特定、先の信号現示を読み出し、列車のスピードを変える。」
くらいはすぐに出来そうな雰囲気です。
そのためのコマンドは全て分かりましたので、あとはアルゴリズムを考えます。
オブジェクト指向など、全く理解しておりませんが、そのままベタで書いていきます。


タグ:DCC Python

ブロックトラッキングの値を読み取り、列車IDに。 [python]

つぎはある区間にいる列車IDを読み取って、
その列車のスピードを変更してみます。

self.a = memories.getMemory("IM:AUTO:0002").getValue()
self.b = int(self.a)
self.throttle = self.getThrottle(self.b, False)
self.throttle.speedSetting = 0.5

こんな感じです。2行目は文字変数を数値に変換しています。
getThrottleの第1引数にint(self.a)と直接書いても構いません。
なんかまどろっこしいですが、メモリー値はテキスト、getThrottleの引数は整数ですので、
こうなってしまいます。
もちろんspeedSettingの値は0から1までの数値ですので、変数を持たせられます。
上手く利用すれば閉塞区間によって最高速度をかえたりもできますね。

これを、2回目でご紹介したスクリプトのクラスの定義部分に書いて、
出来上がりです。

次は信号現示を読み取ります。

タグ:DCC Python

メモリー値を読み出す [python]

JMRI python scriptの2回目、メモリー値の取り出しです。

こちらはPanel Proを開いた状態で、そのままコマンドを入力してOKです。

めもり.png

System NameがIM:AUTO:0002のメモリー値を読むのは
memories.getMemory("IM:AUTO:0002").getValue()
です。
これを調べるのは骨が折れましたが、見つけられれば簡単でした。

出力させないと値が見えませんので、
print memories.getMemory("IM:AUTO:0002").getValue()

一旦変数に持たせても構いません。

a = memories.getMemory("IM:AUTO:0002").getValue()
print a

pythonは変数の方を指定せずにいきなり代入しても良いみたいです。
簡単ですね。

ただ、User Nameではうまく行きません。
たぶん日本語のLocaleを設定してないから、あるいはMemoryを示す接頭語がいるのか、でしょう。
ここは拘らずに、System Nameですませます。

つぎはある区間にいる列車IDを読み取って、
その列車のスピードを変更してみます。

タグ:Python DCC

JMRI python scriptに挑戦 [python]

前回まで、Logixを使って、ポイントや信号機、サウンドの制御をセッティングしてきました。

ここまでやっても、運転士が信号を見落とすと事故になります。
どうせなら、先の信号現示を判定して、強制的に減速、停止できれば良いなぁ、と思うのは自然です。

例えば「前方の閉塞信号機が赤現示のときに、その場所にいる列車を停止させる。」のような動きを設定したいのですが、JMRI Logixを使って、これはできません。
これはある意味デジタルコマンドコントロールの泣き所なんです。

アナログ方式だと、前方信号機に連動させて手前の区間の電圧を制御してやれば、どの列車でも減速→停止が可能なのですが、デジタルだとそこにいる列車のアドレスに対して、コマンドを送ってやらなければなりません。

制御すべき列車のIDはMemory Valueとして保持しておりますので、その値のIDに対してスロットルコマンドを送れば良いのですが、JMRIにはそのためのLogixがありません。何となく、簡単にできそうなんですが。。。ダメです。

そこで、私にとっては敷居の高い禁断の果実を取りに行くことになりました。Jython scriptに挑戦です。

BASIC, Fortran の世代(学生以来)なので、Pythonの流儀が全く分かりません。
そこで、サンプルスクリプトを解読しながらの試行錯誤です。
サンプルはJMRI/jythonのフォルダ内にあります。テキストで開いて、コメントを見ながら解読です。

スクリプト.png

Panel Pro ==> Panels ==> Script Entryで開くテキストフィールドにPython Scriptを打ち込んでみて、
動作を確認していきます。コマンドは打ち込まなくても、Loadボタンで上記JMRI/jythonフォルダから読み込めます。うまくいったスクリプトはセーブも出来ます。
複数行のスクリプトでも打ち込んでおいて、Executeすれば良いです。

結果はScript Outputで確認出来ます。エラーがあればメッセージが出ます。

勉強はLocoTest.pyやAutomatonExample.pyなどのコメントを読みながらです。

ここから以降はプログラムの全くの素人が系統的に学習した訳でなく、我流でサンプルスクリプトを解読、理解した範囲で書いてますので、間違っている場合は是非ご指摘ご教示くださいませ。

解読と試行錯誤の結果、例えばID3の列車のスピードを半分にするにはこれだけのscriptを書く必要があります。(と思います。)

1 import jarray
2 import jmri
3 class test(jmri.jmrit.automat.AbstractAutomaton) :
4
5 def init(self):
6 self.throttle = self.getThrottle(3, False)
7 self.throttle.speedSetting = 0.5
8 return
9 def handle(self):
10 return 0
11
12 a = test()
13 a.start()

(pythonは 左インデントに意味がありますので、ご注意を。ここのサイト、スペースを無視するので困ります。行番号は不要です。良い書き方ないでしょうか。)

スロットルを制御するコマンドは
jmri.jmrit.automat.AbstractAutomaton
で定義されていて、これらのコマンドをつかうには、
クラス(コマンド?)を定義して(3~10行)、実行(12~13行)します。

5〜8行、def init から returnまでは最初に一度だけ実行する部分、ここに全部書いてあります。実際に実行するのは2行だけなんです。
def handle から returnまでは、ルーチンに回す部分ですが、return 0で即終了です。return 1だと回り続けますし、判別式を入れておけば条件によって終了させることが出来ます。

自分の場所を読み取って、先の信号を読み取って、、、、結構大変そうです。。。。
タグ:Python DCC

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。