水城珠洲のページ

ATS.DLLの作り方

BVE4のATSプラグインを作る

2005/01/18
2005/01/19
2005/01/20
2005/01/23
2005/01/25
2005/02/17
2013/09/01

BVE4からATSプラグインを作成することが出来るようになりました。
ATSプラグインをある特定のインタフェースにあわせて作成すれば動作します。
ここでは、ATS-Snを実際に作成し、その作り方を学びます。

お詫び:目次もまだ簡単なものしかありません…
サンプルソースはこちら(05/01/25版)(本文中にも出てきますが、ショートカッ ト…)
試験環境は本文中にあります。
BVE4向けATS.DLLの作成方法(進化中?)(第四版)もあわせてごらんくだ さい。


目次


・前 書き(ごたく)
ATSプラグインを作成すると言うことは一つのアプリケーションを作成す ることと大差ありません。
従って、設計、作成、試験の工程を踏む必要があります。
というと、大変なように聞こえますが、まぁ、肩の力を抜いて楽~~に行きましょう。
(ちなみに、わたくしはかなり雑な作り方をしてます。人によっては、話にならないと思う場合があると思いますが、ご了承ください。)
なお、前もって必要なものを列挙してます。
・根気とやる気
・C言語かC++言語の最低限の知識
・DLLが作成できる環境(何でも動くんじゃないかなぁ?当方は無難なVCを使います)
・試験できる環境(BVE4が動く環境)
・ATSの知識(私のレベルで作れるんだから普通のマニアさんなら楽勝じゃないかな?)
・あとは、以下の文章を読んで解るぐらいの理解力があれば十分?
・嫌気がさしたらやめる勇気(ぉ


・設計
列車が保安装置によってどんな動きをするかをまずまとめます。イベントと状態遷移が出てきます。
ATS-Snは
前方の信号が赤の時に、Sロングの地上子上を通過するとATSがなる。(白消灯、赤点灯)
5秒以内にブレーキを常用に入れて確認ボタンを押下しないと非常ブレーキ動作(白消灯、赤点滅)
確認ボタンを押下すればATS確認状態になる。(白点灯、赤消灯)
その後、ATS取り扱い終了をすると静かになる。(白点灯、赤消灯)
絶対信号で赤の時に、信号直下にある地上子上を通過すると非常ブレーキ動作(白消灯、赤点滅)

簡単にまとめるとこういうことです。
これを図にまとめてみました。
図1
図1 ATS-Snの実装

これで列車側の動作はぱーぺきです。(死語)
これをどのようにプラグイン上に載せるかという段階に話が進みます。


ATSプラグインのBVEにおける実装(つまり、ATSプラグインって 何?ってはなし)
以下の関数が求められます。

  • Load : DLLがローディングされたときに呼び出されます。初期化に使います。
  • Dispose : DLLがリリースされるときに呼び出されます。後始末に使います。
  • GetPluginVersion : 対応しているプラグイン版数を返します。現在は00020000Hです。
  • SetVehicleSpec : 列車データが読み込まれたとき呼び出されます。列車の諸元が渡されます。
  • Initialize : ゲーム開始時に呼び出されます。ブレーキングの設定が渡されます。
  • Elapse : 1フレームごとに呼び出されます。様々な設定を返すことによりゲームを制御できます。
  • SetPower/SetBrake/SetReverser : ユーザがハンドルの制御を行うと呼び出されます。
  • KeyDown/KeyUp : ATS関連のキー操作で呼び出されます。
  • HornBlow : フォーン(警笛)をならすと呼び出されます。
  • DoorOpen/DoorClose : ドアの開け閉めで呼び出されます。
  • SetSignal : 次の閉塞の入り口にある信号の状態が遷移すると呼び出されます。
  • SetBeaconData : 地上子の上を通過すると呼び出されます。通過した地上子の状態が渡されます。

こうしてみると、総てイベント制御されているのですが、種類によって分けられます。
・一発もの(立ち上げてからユーザが操作できるようになるまでに呼び出されるもの)
Load/Dispose/GetPluginVersion/SetVehicleSpec/Initialize

・制御関数
Elapse

・ユーザ操作関数
SetPower/SetBrake/SetReverser/KeyDown/KeyUp/HornBlow

・イベント関数
DoorOpen/DoorClose/SetSignal/SetBeaconData

一発ものは余り意識しなくても最初にくんでしまえばいいのです。
列車の制御はElapseを用いて行います。制御に対して必要なイベントをユーザ操作関数やイベント関数から回収し、状態を保存し、制御関数で実際に列車 データへ反映するという動きになります。

ユーザの操作や起こるイベントを書き出すと…
・主ハンドル制御(A/Z)
・ブレーキ制御(,/./スラッシュ)
・リバーサ制御(Up/Down)
・警笛(Return)
・ATS確認扱い(Space)
・ATS確認終了(Ins)
・復帰(Home)

意識するべきイベントは
・ドア開け
・ドア閉め
・地上子上通過

となります。そこで、このイベントを関数と、先ほどまとめた動作と関連づけていきます。


意識するものだけを取り出しました。

・主ハンドル制御
プラグイン関数:SetPower
動作:プラグイン内の状態構造体へ反映する。
例外:戸閉め状態では列車にはOFFを返すこと。

・ブレーキ制御
プラグイン関数:SetBrake
動作:プラグイン内の状態構造体へ反映する。
例外:非常ブレーキ動作状態では列車には非常ブレーキを返すこと。

・リバーサ制御
プラグイン関数:SetReverser
動作:プラグイン内の状態構造体へ反映する。

・ATS確認扱い
プラグイン関数:KeyDown – ATS_KEY_S
動作:プラグイン内の状態遷移を行う
ATS警報状態からATS扱い状態へ
それに伴う音声処理を行う

・ATS確認終了
プラグイン関数:KeyDown – ATS_KEY_A1
動作:プラグイン内の状態遷移を行う
ATS扱い状態から通常状態へ
それに伴う音声処理を行う

・復帰
プラグイン関数:KeyDown – ATS_KEY_B1
動作:プラグイン内の状態遷移を行う(内部の初期化)
非常ブレーキ動作状態から通常状態へ
それに伴う音声処理を行う

・ドア開け
プラグイン関数:DoorOpen
動作:プラグイン内の状態構造体へ反映する。

・ドア閉め
プラグイン関数:DoorClose
動作:プラグイン内の状態構造体へ反映する。

・地上子上通過
プラグイン関数:SetBeaconData
動作:プラグイン内の状態遷移を行う
Sロングの場合、信号情報を確認し赤であったら、警報状態に遷移する。
信号直下の場合、信号情報を確認し赤であったら、非常ブレーキ動作状態に遷移する。

そのほかの関数はRETURN応答のみとします。

これで大幅の道筋は出来ました。


次に必要となる関数と、構造体を定義します。

必要関数
音声制御
ats_bellControl(int bell1,int bell2); <MACRO>
ベルの制御を行います。この関数で設定した次のElapse関数で反映されます。
引数にはbell1にATS警報音,bell2にATS持続音の設定を行います。設定値はプラグインに渡す値と同じです。

表示灯制御
ats_lampControl(int lamp1,int lamp2); <MACRO>
表示灯の制御を行います。音声制御と同等です。
なお、2個では明らかに足りてないのですが、サンプルソース用途言うことでご了承ください。

ATS状態制御
void ats_statusSet(int enableStatus,int disableStatus);
ATSの状態を制御します。有効にしたい状態をenableStatusに、無効にしたい状態をdisableStatusに設定します。

ATS状態確認
int ats_statusChk(int chkStatus);
確認したい状態を指定します。0(偽)ならその状態にない、0以外(真)ならその状態にあります。
ただし、通常状態のみは取れません。それ以外の状態を見るように使用してください。

構造体:ATS状態共通エリア

typedef ats_Status{
	int nowHandle;		// 主ハンドル制御
	int nowBrake;		// ブレーキ制御
	int nowReverse;		// リバーサ位置
	int *lampCnt;		// ランプ制御
	int bell[2];		// ベル二種
	int nowTime;		// 現在時間
	int atsTime;		// ATS計測用時間
	float speed;		// 速度
	int doorStat;		// ドア状態
	int atsStatus;		// ATS状態
};

ATS状態は以下のOR値とする。

ATS_NORMAL   0x0000  // 通常状態
ATS_ALART    0x0001  // 警報状態
ATS_CHECK    0x0002  // 取り扱い状態
ATS_HALT     0x0004  // 非常ブレーキ動作状態

・作 成
いよいよ作成です。たぶん、おおかたの人にとってこの工程が楽しいですね。
当方の開発環境を示します。かなりヘチョイです。
マシン:VirtualPC for Windows in ThinkPad 650E
CPU:Pentium!!! 500MHz(ホストマシン)
Memory:割り当て128M
OS:WindowsNT4 SP6a
環境:VC++6

このマシンをLANでつなぎ、テスト用の車両データのディレクトリを共有設定してコンパイルしては投げ込んで動かしています。
なお、当方の環境はNT4ですが、おそらく2000とかでも大丈夫だと思います。


まず、作業用の適当なディレクトリを作っておいてください。また、VCを 立ち上げて、DLLの作成を選びます。
次に、プロジェクト(*.dsp / *.dsw)のあるディレクトリの下に”src”ディレクトリを掘っておいてください。(好みの問題ですが)
作成したら、BVEの公式サイトで配布されている「atsplugin.h」というファイルを放り込んでおきます。また、適当な名前で拡張子がcppの ファイルを作成します。これが、今回のソースファイルとなります。私は、atssn.cppとしました。
あとは、ファイルの中をゴリゴリコーディングして形にしていきます。(C言語やC++言語の知識が要求されます。この辺りは、各自勉学に励み、身につけて ください。)
リソースファイルで版数をつけておけば管理が楽ですね。そうそう、DEFファイルによる明示的な宣言もお忘れなきように。


まず立ち上げから作ってしまいます。立ち上げないと他の部分も楽しめない し。
立ち上げ部→Load/Dispose/GetPluginVersion/SetVehicleSpec/Initialize
この中で、必要なものはLoad/GetPluginVersion/SetVehicleSpecです。


・Load
作業領域の獲得と、初期化を行います。メモリの空間はまず所定の値に初期化しないとどんな値が入っているか解りません。書く前に読んでしまうと、あらぬ 値(不正な値)が入っている可能性があります。
そこで、まず、
→ATS状態共通エリアのランプ制御エリアの領域を獲得。
→全領域を0クリア
→音声情報エリアをSTOPに再設定
→ランプ制御エリアの固定値エリアに固定値を設定
→列車情報領域の0クリア(後述:SetVehicleSpec)
→列車生業領域の0クリア(後述:Elapse)
を行います。

なお、ここでメモリの獲得を行うので、必然的にDisposeが必須となります。Disposeで獲得した領域を解放しましょう。


・GetPluginVersion
この関数ではATS_VERSIONを返してやればいいだけです。


・SetVehicleSpec
領域を作成しておいて、そこに情報を保存してやればいいです。この中に、常用ブレーキの段数(何処からが常用ブレーキか)、ブレーキの段数など主要な情 報が入っています。非常ブ レーキはブレーキ段数+1になっています。これ重要。


次にElcapeの大まかな動きを決定しておきます。

  1. そのときの時刻を保存します。(地上子通過時に使用します)
  2. 状態を確認する。
    1. ATS警報状態であれば、時間を確認し経過時間が5秒を超過したら非常ブレーキ動作状態へ
    2. 非常ブレーキ動作状態であれば0.5秒毎に赤灯を明滅させます(時間を500で割って、奇数か偶数かを見る)
  3. 主ハンドル状態を反映します。
    このとき戸閉め状態であれば、反映する値は0固定にします。(動かないように)
  4. ブレーキ状態を反映します。
    このとき非常ブレーキ動作状態であったら、反映する値は非常ブレーキ固定にします。(解除させない)
  5. 音声情報とパネル情報を反映します。

次にユーザの操作についての動作を決定しておきます。なお、代入するだけ のハンドル系は特に記しません。省略します。
残るのはKeyDownです。これもそんなに複雑じゃありませんが、説明します。

入力されるキーによって動作を分けます。

  1. SPACEが入力された。
    状態がATS警報状態でありブレーキ操作が常用ブレーキになっていれば、状態をATS取り扱い中に遷移させます。
  2. Insertが入力された。
    状態がATS取り扱い中であれば、通常状態に遷移します。
  3. Homeが入力された。
    状態が非常ブレーキ動作状態でかつブレーキ操作が非常ブレーキに移動していたら、リセットを行い通常状態に遷移します。

難しくないでしょう?あとは、これらを言語に落とすだけなんです。


地上子通過のSetBeaconDataの動作を決定します。これで最後 ですね。

通過する地上子で動作が変ります。

  1. Sロング上を通過
    対になる信号の情報を確認(引数でわたってくる)し、停止(赤、0)であればATS警報状態に遷移します。
  2. 信号直下上を通過
    対になる信号の情報を確認(引数でわたってくる)し、停止(赤、0)であれば非常ブレーキ動作状態に遷移します。

この二つで、分岐器速度制限装置以外は再現できます。


実際にコーディングしてみたソース/バイナリはこちら(05/01/25版)。使用条件は同梱の ファイルを参照してください。
なお、ANSI-C準拠ちっく(一部C++拡張を使ってますけど)にて実装されています。
(作者様のmackoyさんのats1.dllはC++による素晴らしい実装です)


・試験
一番辛くてやりたくない工程がこれですね。設計で上げた機能に対する試験。実装に上げた機能に対する試験。これを実施しま しょう。
試験項目書を簡単に作ってチェックしながらやります。また、DLLは実際のデータに読み込まれてしまうと画面上に見えないので、仮想的にDLLを呼び出す 試験ドライバを作って試験します。試験にパスしたから動くというものでもないのですが(自分の想定できないところで不具合は出るものですから)。

試験の項目は実運用試験(実際に使ってみて不具合を見る試験)、設計項目に対する試験(モジュールを結合して試験することから結合試験と呼ばれます)、関 数の動作を確認する試験(コーティングに対する試験であるためコードテスト、または結合せずに単体で試験することから単体試験と呼ばれます)の三段階に分 かれます。

実運用試験→実戦投入
設計項目試験→思想に対し動作が正しいか確認する
関数動作試験→正しく組めたかを確認する
と考えると分かりやすいのでは?

今回の作成に対し項目をざっと作ってみたので確認してください。(PDFです)
簡単な試験項目<ats_test.pdf>

当然のことながら、ATS.DLLを試験するドライバも作らなくてはなりません。
DLLをロードしてアドレスを取得し、各関数を呼び出す簡単なプログラムの作成も必要です。(この試験用プログラムをドライバ(運転者)といいます)
試験環境(設計観点のみでかつ大項目3は未実装…雰囲気程度にみてください。改善の余地は大い にあります。)
技術的なことは省きます。この環境は設計観点ですが、作成観点ではDLLにせず、ソースをリンクして試験しましょう。じゃないと追跡するのに骨となりま す。

ポイントとして、関数動作試験、設計項目試験をすり抜けて実働試験で不具合が起きた場合。BVEが動作しているとプログラムの動作は覗けません。この場合 は、fopen系の関数を使ってパラメタをファイルに吐き出すのも手です。まぁ、綺麗じゃありませんがね。ロートル駄目プログラマの戯言程度として聞いて おいてください。


・最後に
さて、ここで、皆様に課題がございます。
以下に仕様を提示するので、これで分岐器速度制限装置を再現してください。

ATS-Sn区間で採用される分岐器速度制限装置であるATS-SN形速度照査装置は次のような動作をします。
およそ枕木二本分の区間に二つ設置され、二つを通過する時間から列車の速度を決定しそれにより警報するか?非常ブレーキをかけるか?を判断します。
ATS-SN形速度照査装置の開始を地上子種別55、終わりを56として実装してみてください。細かいことはネットで調べましょう。(図書館に行ってにら めっこしてもいいと思います)照査用の速度については路線データからパラメタでもらってください。
福島交通で使っている終端駅用の速度照査装置が同じものです。


以上です。

こんてんつめぬー

こんてんつめぬー

  • facebook
  • twitter
PAGETOP
Copyright © Suzu Minashiro All Rights Reserved.
Powered by WordPress & BizVektor Theme by Vektor,Inc. technology.