Nゲージ鉄道模型のATS・ATC案③-LEDモジュール

この記事は過去に作ったNゲージ鉄道模型ATCシステムについて記述しています.
システム全体については下の過去記事を参照してください.

sosoru.hatenablog.jp


f:id:sosoru_m:20151004222219g:plain

LEDモジュール

LEDの制御もユーザーの端末で出来るようにしていました.ATCの動作に直接の関係はありません.当時は駅ホームの照明に使っていました.(本当は信号に使うべきかもしれない)

ハードウェアについて

f:id:sosoru_m:20151018122725p:plain

Dフリップフロップ(D-FF)とバッファを組み合わせた(恐らく)ポピュラーな回路構成です.D-FFはクロック入力CLKが立ち上がった時,入力Dの状態が出力Qに保存され,次にCLKに変化あるまで,出力Qは状態を保持し続けます.幾つかのD-FFを組み合わせ,それぞれの入力を共有しておき,各D-FFことにCLKを制御することで,マイコンが扱えるIOピンが増えます.このモジュールでは8入出力D-FFを4つ使っています.

DフリップフロップICの出力ピンに流せる電流が心許ないので,バッファを入れて大電流に備えています.

ちらつかない程度な低周波の128段階PWM波形を生成し,一つのモジュール当たり,32個のLEDを駆動することが出来ます.実際には出力を30個に切っています.これは,出力コネクタにパラレルATAケーブルを流用している都合です.

マイコン ATmega1284P
D-FF TC74VHC574F
バッファ TD62783APG

f:id:sosoru_m:20151018143134p:plain

ソフトウェアについて

マイコンは20MHzで駆動しており,通信基板とSPIで会話しつつ,ソフトPWMをこなします.下にコードを貼ります.(いま見ると命名がひどいなぁ)

#include "avr_base.hpp"
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
#include <Timer.h>
#include <string.h>
#include <tus.h>
#include <led_packet.h>
using namespace AVRCpp::Timer;

#define PORT_DATA			PORTA
#define PORT_CLK			PORTD
#define PORT_ENABLE			PORTC
#define PORT_ENABLE_NUM		0

#define LED_COUNT			30

LedState g_States[LED_COUNT];
DeviceID g_myDevID;

uint8_t led_count_remains[LED_COUNT];
uint8_t led_count_init[LED_COUNT];

#define APPLY_TO_LED(clk,ind) \
if(led_count_remains[(clk+1)*8-ind-1] >0){ \
	sbi(led_signal_buffer, ind); \
	led_count_remains[(clk+1)*8-ind-1]--; \
}else{ \
	cbi(led_signal_buffer, ind);} 
				
ISR(TIMER1_COMPA_vect)
{
	tus_spi_process_packets();
}

void device_init()
{
	DDRA = 0xFF;
	DDRD = 0xFF;
	DDRC = 0xFF;
	
	cbi(PORT_ENABLE, PORT_ENABLE_NUM); // enable D-flipflops
		
	memset(led_count_init, 0, sizeof(led_count_init));
	memset(led_count_remains, 0x00, sizeof(led_count_remains));
}

void init_led_status()
{
	uint8_t cache = SREG;
	cli();
	memcpy(led_count_remains, led_count_init, sizeof(led_count_init));
	SREG = cache;
}

void apply_led_status()
{
	uint8_t led_signal_buffer;
	
	APPLY_TO_LED(0,0);
	APPLY_TO_LED(0,1);
	APPLY_TO_LED(0,2);
	APPLY_TO_LED(0,3);
	APPLY_TO_LED(0,4);
	APPLY_TO_LED(0,5);
	APPLY_TO_LED(0,6);
	APPLY_TO_LED(0,7);
	PORT_DATA = led_signal_buffer;
	PORT_CLK = 1<<0;
	
	APPLY_TO_LED(1,0);
	APPLY_TO_LED(1,1);
	APPLY_TO_LED(1,2);
	APPLY_TO_LED(1,3);
	APPLY_TO_LED(1,4);
	APPLY_TO_LED(1,5);
	APPLY_TO_LED(1,6);
	APPLY_TO_LED(1,7);
	PORT_DATA = led_signal_buffer;
	PORT_CLK = 1<<1;
	
	APPLY_TO_LED(2,0);
	APPLY_TO_LED(2,1);
	APPLY_TO_LED(2,2);
	APPLY_TO_LED(2,3);
	APPLY_TO_LED(2,4);
	APPLY_TO_LED(2,5);
	APPLY_TO_LED(2,6);
	APPLY_TO_LED(2,7);
	PORT_DATA = led_signal_buffer;
	PORT_CLK = 1<<2;
	
	//APPLY_TO_LED(3,0);
	//APPLY_TO_LED(3,1);
	APPLY_TO_LED(3,2);
	APPLY_TO_LED(3,3);
	APPLY_TO_LED(3,4);
	APPLY_TO_LED(3,5);
	APPLY_TO_LED(3,6);
	APPLY_TO_LED(3,7);
	PORT_DATA = led_signal_buffer;
	PORT_CLK = 1<<3;
	PORT_CLK = 0;
}

bool ProcessLedPacket(LedState *pstate)
{
	if(pstate->Base.ModuleType != MODULETYPE_LED
		&& pstate->Base.InternalAddr > LED_COUNT
		&& pstate->Base.InternalAddr == 0)
		return false;
	
	led_count_init[pstate->Base.InternalAddr-1] = pstate->DutyValue;
	
	return true;
}

void spi_received(args_received *e)
{
	g_myDevID.raw  = e->pdstId->raw;

	ProcessLedPacket((LedState*)e->ppack);
}

int main(void)
{	
	uint8_t i;
	MCUCR = 0b01100000; //todo: turn off bods
	MCUSR = 0; // Do not omit to clear this resistor, otherwise suffer a terrible reseting cause.
	
	tus_spi_init();
	tus_spi_set_handler(spi_received);
	
	device_init();
	TimerCounter1::SetUp(NoPrescaleB, Normal16, NormalPortOperationA, NormalPortOperationB, Off, Fall);
	TimerCounter1::CompareMatchAInterrupt::Enable();

    while(1)
    {	
		init_led_status();
		for(i=0; i<128; ++i)
		{
			apply_led_status();
			_delay_us(100);
		}
	}
}

通信基板との会話

別ファイルにSPI割り込みの関数が定義されていて,データの送受信を行っています.
タイマ割り込みでtus_spi_process_packets()が呼び出されており,定期的に受信したデータの処理を行っています.この関数は,事前にtus_spi_handler()で指定された関数を間接的に呼び出し,具体的な処理を委ねています.このモジュールではspi_received()が設定されています.spi_received()は受信したデータを実際に反映していて,LEDのDuty比設定を更新しています.

ソフトPWM

led_count_initに設定するDuty比を書いておいて,周期の最初,led_count_remainsにled_count_initをコピーします.
ループ毎にled_count_remainsをデクリメントし,0になるまでオンにしておきます.この処理に相当するのがapply_led_status()です.
これが最も時間的な制約を受けそうですが,コンパイル時に-O3で最適化を書けた場合で,最悪でも大体200クロックで抜けるようです.これは,クロック周波数が20MHzだとすると,10usec程度で処理か終わる見込みです.
直後の100usecのdelayを考えると,1周期は約14msecぐらいで回り,PWM周波数は71Hz程度と予測できます.SPIの割り込み等を考えると,もう少し遅くなっているでしょう.

おわり

あとで写真を撮って貼っておきたい
本当はポイントモジュールが先だったけど,設計がちょっと疑問なので実測してから載せる予定

Nゲージ鉄道模型のATS・ATC案②-閉塞・モータモジュール

sosoru.hatenablog.jp

f:id:sosoru_m:20151004222219g:plain

モータモジュールについて説明します.

モータモジュール

f:id:sosoru_m:20151004222919p:plain
↑ブロック図

f:id:sosoru_m:20151004222347p:plain
↑基板

主要な部品:

マイコン ATmega1284P
モータードライバ TB6559FG
リレー 941H-2C-12D


モータモジュールで担当している閉塞のレール電圧・電流を管理します.
マイコンのADCはモータードライバを貫く電流を見ています.シャント抵抗の電圧を25倍のアンプで増幅した電圧から判断しています.
リレーは,担当する閉塞のレールと,モータドライバの出力を繋ぐか,隣接する閉塞のレールを繋ぐかを制御しています.開放時には隣接する閉塞,短絡時にはモータドライバを接続しています.

モータモジュールの動作モード

このモータモジュールには,大きく分けて4つのモードが存在します.
モータドライバのHブリッジの様子です.

  • スタンバイモード

リレーを開放し,隣接する閉塞と担当する閉塞が接続されている状態です.この時,このモジュールのモータードライバは仕事をしません.

  • 正方向ONモード

リレーが短絡され,モータドライバと担当する閉塞が接続されています.モータードライバはレールと接続され,列車に電流が供給されます.
f:id:sosoru_m:20151004224310p:plain

  • 逆方向ONモード

正方向ONモードと逆方向に電流が供給されます.
f:id:sosoru_m:20151004224321p:plain

  • ブレーキモード

正方向・逆方向ONモードと同様にリレーが短絡されていますが,モータードライバは電流を供給せず,どちらのレールもグランドと接続されます.この時,レールはどちらともグランドに短絡した状態です.
f:id:sosoru_m:20151004224331p:plain

この4つの動作モードを使い分けて閉塞動作を実現します.

閉塞遷移について

列車の進む向きで若干挙動が変わるので,正方向時と逆方向時を分けて説明します.

正方向時

閉塞遷移前

f:id:sosoru_m:20151004224947p:plain
左モータモジュールが担当する閉塞上の列車を駆動しています.次の閉塞を担当している右モータモジュールはブレーキモードで待機しています.矢印は電流を表しています.

閉塞遷移中

f:id:sosoru_m:20151004225434p:plain
列車の「車輪」が次の閉塞をまたごうとする瞬間,次の閉塞のモータモジュールには前の閉塞と短絡します.この時に流れる電流をモータモジュールは検知します.

閉塞遷移後

f:id:sosoru_m:20151004225746p:plain
電流を検知したモータモジュールはスタンバイ状態に移行します.列車は前の閉塞のモータモジュールから電流が供給され,移動し続けます.

逆方向時

閉塞遷移前

f:id:sosoru_m:20151004230009p:plain
正方向時とは逆に,右モータモジュールが担当する閉塞上の列車を駆動しています.同様に,左モータモジュールはブレーキモードで待機しています.

閉塞遷移中

f:id:sosoru_m:20151004230147p:plain
同様に,車輪で前の閉塞と短絡したときに流れる電流を検知します.左モータモジュールはブレーキモードから逆方向ONモードに移行します.この時,左右どちらのモジュールも逆方向ONになります.
同一のマイコンであれば問題ありませんが,異なるマイコンだと,クロック源が異なるのでPWM波形の位相がズレます.この波形のズレは「うねり」となって現れ,走行に問題を引き起こします.この問題は通電カプラーを用いている編成には顕著に表れ,閉塞をまたぐときだけ速度が上昇し,室内灯の明るさに「うねり」が現れます.
この問題を防止するため,左のモータモジュールは逆方向ONモードになった後,速やかに右の閉塞に「スタンバイモード」へ移行するよう命令します.

閉塞遷移後

f:id:sosoru_m:20151004231036p:plain
右のモータモジュールがスタンバイに移行した後,列車は運行を継続します.

閉塞遷移後の処理について

正方向・逆方向ともに,閉塞遷移した後,今までいた閉塞の開放処理をしません.閉塞の開放処理は「制御・Webサーバー」が担います.制御・Webサーバーは列車が抑えている閉塞数を監視し,一定数以上,閉塞を抑えたとき,末尾の閉塞を開放します.

この方法であれば,フィーダだけで閉塞を形成できますが,各列車は最低2閉塞占有することが問題です.これは閉塞数を増やすことでカバーしています.

閉塞の継ぎ目を列車がまたぎ終えたことを光センサ等で監視すれば,この問題を解決できますが,レイアウト形状に任意性を持たせるために導入しませんでした.

Nゲージ鉄道模型のATS・ATC案①-概要

数年前に作ったNゲージ鉄道模型の閉塞システムについて,あまり資料をまとめていなかったので,ここにまとめます.

動機

2010年頃,私はある大学のサークルの鉄道模型展示に関わることになりました.そこでは当時,8mX6mぐらいの規模で4線エンドレスのレイアウトを作成していました.
このレベルの規模だと,どれだけ長い編成でも中々列車が巡ってきてくれず,見ていて少し寂しい印象を受ける欠点がありました.
これを解決するために,私は1線で2編成以上の運行を行うため,閉塞システムを作ることになりました.

2編成以上動かすのであれば,DCCを導入すれば負担が軽減出来そうですが,走らせる車両は部の所有の車両ではなく,部員が持ち寄った車両ということで,車両への加工が必要なDCCは導入できませんでした.
そこで,リレー式の閉塞システムを作ることにしました.

全体図

f:id:sosoru_m:20151004214922g:plain

レールやポイントを操作するハードウェアは「モジュール」にまとめられています.これらのモジュールは「通信基板」とルーターを通してそれぞれが通信できるようになっています.
「制御+Webサーバー」は閉塞制御行うようにモジュールに指示を飛ばし,ユーザーの入力を処理します.
「ユーザー」はタブレットやラップトップといった,Webブラウザが動く端末であればどれでも,列車の制御を行えます.

目次

  • モータモジュール

sosoru.hatenablog.jp

  • ポイントモジュール
  • LEDモジュール

sosoru.hatenablog.jp

  • Web・制御サーバー
  • WebUI
  • 在線表示ページ
  • 電車でGOコントローラの利用

ElecrowからPanelizing(面付け)した基板が届いた

作成した基板について


f:id:sosoru_m:20150911184429p:plain

この基板パターンを100x94mmに5つ面付けする.

f:id:sosoru_m:20150911185235p:plain

Panelizingについて

Elecrowの基板作成サービスにはPanelizingなる項目がある
f:id:sosoru_m:20150911183303p:plain

一つの基板に幾つかの基板パターンを入れて,Vカットの細い溝で区切ってくれるらしい.小さい基板を大量に欲しいときには割りと使えそう.

面付けに関する詳しいことは下記ページにある.基板サイズの制約や注意事項等々がある.
BLOG | PCB Panelize is Available now

その入れ方については公式wikiが説明してくれている.
How to panelize PCBs with CAM350 - Elecrow

  • 面付けを行うためには8cm*8cm以上でなければならないとある.今回は5つ縦に置いても70x90mmなので条件を満たさない.8cm*8cmを下回る場合にはTechnology Edgeなる余白を3mm以上入れなければならない.
  • Vカットを入れる切り取り線はシルクスクリーンに書き込む.基板パターンの間隔は1.6mmの基板厚さだと,0.8mmぐらい空ける必要がある.今回は1mmの間隔を置いている.
  • 注文フォームのPanelizingには幾つか種類があって,面付けする基板パターンの種類や個数によって追加料金が異なるらしい.とりあえず「Single PCB with milling」で注文したら,あとで$8払ってくれメールが届いた.$8は「2-5 copies」に相当する.

基板到着までの流れ

Elecrow

2015/08/30に注文.とりあえず,追加情報に「ボトムシルクに切り取り線入れたからV-cutしてね」と書き込む

2015/08/31に追加料金の旨に関するメールのやりとり.その日のうちに「In Production」に進む

2015/09/07に「Traceable」に変更.製造に5~6営業日かかったのかな?(面付けでちょっと多く掛かるとは書いてある)

DHL

DHLの配達記録.日本語だと空欄になってるところがあったので,英語版を載せる.1~20番までは1日ちょっとで来てるからとても早い.
「Forwarded for delivery」は悪名高き「配達業者への荷物引渡し準備完了」,佐川急便に投げるってやつ.運が良いとDHLが直接届けてくれて,Traceableから1日で来るんだけど残念.

Friday, September 11, 2015 Location Time
21 Delivery attempted; recipient not home TOKYO - JAPAN 17:38
Tuesday, September 08, 2015 Location Time
20 Forwarded for delivery TOKYO - JAPAN 14:20
19 Arrived at Delivery Facility in TOKYO - JAPAN TOKYO - JAPAN 12:37
18 Departed Facility in TOKYO - JAPAN TOKYO - JAPAN 11:36
17 Processed at TOKYO - JAPAN TOKYO - JAPAN 11:28
16 Clearance processing complete at TOKYO - JAPAN TOKYO - JAPAN 10:48
15 Arrived at Sort Facility TOKYO - JAPAN TOKYO - JAPAN 9:55
14 Customs status updated TOKYO - JAPAN 9:07
13 Transferred through TOKYO - JAPAN TOKYO - JAPAN 8:50
12 Departed Facility in HONG KONG - HONG KONG HONG KONG - HONG KONG 3:35
11 Processed at HONG KONG - HONG KONG HONG KONG - HONG KONG 3:33
10 Clearance processing complete at HONG KONG - HONG KONG HONG KONG - HONG KONG 0:44
9 Arrived at Sort Facility HONG KONG - HONG KONG HONG KONG - HONG KONG 0:36
Monday, September 07, 2015 Location Time
8 Shipment on hold HONG KONG - HONG KONG 23:35
7 Clearance processing complete at SHENZHEN - CHINA, PEOPLES REPUBLIC SHENZHEN - CHINA, PEOPLES REPUBLIC 22:00
6 Customs status updated HONG KONG - HONG KONG 18:23
5 Clearance event SHENZHEN - CHINA, PEOPLES REPUBLIC 18:00
4 Departed Facility in SHENZHEN - CHINA, PEOPLES REPUBLIC SHENZHEN - CHINA, PEOPLES REPUBLIC 17:22
3 Processed at SHENZHEN - CHINA, PEOPLES REPUBLIC SHENZHEN - CHINA, PEOPLES REPUBLIC 17:16
2 Arrived at Sort Facility SHENZHEN - CHINA, PEOPLES REPUBLIC SHENZHEN - CHINA, PEOPLES REPUBLIC 15:58
1 Shipment picked up SHENZHEN - CHINA, PEOPLES REPUBLIC 12:08

佐川急便

配達記録.DHLの荷物番号とは紐付けされていないので,荷物が届かないと記録は見られない.

⇒ 2015年09月11日 17:47 配達は終了致しました。
↑ 2015年09月11日 17:38 ご不在でしたので、お預かりしております。
↑ 2015年09月11日 八王子営業所から配達に出発致しました。
↑ 八王子営業所でお預かりしております。
↑ 2015年09月08日 19:31 府中営業所を出発致しました。
↑ お荷物をお預かり致しました。

日本とシンセンよりも府中と八王子の方が遠いらしい.台風だったからね,仕方ないね.

届いた基板

f:id:sosoru_m:20150911194515j:plain

切れてる基板が10枚届いた.隣のは同時に頼んだ普通の基板.Technology Edgeなるエリアには3つの穴とハンダで埋まったスルーホールがある.

f:id:sosoru_m:20150911194736j:plain

Vカットされると思ったけど,ドリルで切ってくれたっぽい.シルク線を別々に書いたからかな?これなら基板外形線で書いても良かったかも.

f:id:sosoru_m:20150911195611j:plain

Vカットのところを横から見るとこんな感じ.場合によっては,端面をヤスリでちょっと削らないといけないかもしれない.

  • 5つ面付けした基板10枚セット(10x10cm)で$23.9だったので,一枚当たり$0.478.日本円で50円ちょっとかな.面付け無し10x5cmで頼むよりは安いですね.
  • Elecrowの基板色はBlackの他にMatte Blackなるオプションがある($20).確かに今回の基板はちょっとくすんでる(縦のスジが目立つ感じ).Matte Black + ENIGなら相当映えるパターンの基板が出来そうだなぁ.

納涼?「すぐにけせ」を再現するUEFIバイナリを作ってみた

「すぐにけせ」?

f:id:sosoru_m:20150819024045j:plain
オカルト都市伝説の一つ,SFCで発売された「真・女神転生(II?)」を起動すると1/65536の確率で表示されるメッセージのことです.
これは広く「ガセネタ」として認識されています.私自身も,SFCは疑似的に乱数を発生させて(る気がし)ますので,起動時にランダムで表示するのは厳しいと思ってます.(セーブデータが消えたら,とか,別のトリガーで出てきたら面白いですけどね)

UEFIとは?

マザーボードに乗っかってるファームウェアのインタフェースです.最近のマザーボードUEFIを実装していて,UEFISDKも公開されているので,OS起動前の処理を比較的簡単に書けたりします.
今回はこれを使って,起動前にメッセージを表示させています.

ポイント

とりあえず,周知の事実から「すぐにけせ」は「起動時」に「1/65536で再現」することが出来れば良さそうです.

「起動時」

起動時に再現するためには,UEFIに関わらず,MBRブートした小さなLinuxに表示させれば良いんじゃないかなぁとも思いました.表示する判定の場合は表示して,表示しない判定の場合には,次回ブートするデバイスをBIOSに伝えればいいわけです.ただ,メッセージを表示しない確率が非常に高いはずなので,毎回起動時に2回ブートさせるのはちょっと微妙だなぁと思いました.
僕のマザーのブートマネージャだけかもしれませんが,UEFIブートであれば,起動に失敗した場合,すぐに次の起動プログラムを試してくれるようです.これを悪用しない手はありません.最初に「すぐにけせ」バイナリを起動させて,2段目に使用するOSを起動するように設定すれば,違和感なく起動時に「すぐにけせ」判定をさせることが出来ます.

「1/65536」

起動時に確率で発生させることはSFCでは出来ないはずです.しかし,我々の使っているモダンなコンピュータはRTCが内蔵されているので,起動時でも問題なく乱数を発生できそうです.
UEFIで日付を手に入れようとするには,RuntimeServicesのGetTime()を使えば良いのですが,渡されるのはEFI_TIME構造体で,○○年元旦からの積算秒で表しているわけではなく,年月日等々が直感的に分かる仕様*1でした.
時分秒とミリ秒が分かるようなので,ミリ秒ベースに変換して65536で割っています.お手軽実装です.ミリ秒だけは電源投入時から計測しているような気がして不安ですが.

画像について

複雑なファイルシステムを扱えるのがUEFIの売りな気がしますが,表示させる画像はリテラルconst unsigned char [] に直してソースコードに埋め込んでいます.
GraphicsOutputで表示させるために,BMP画像を変換してくれる関数がEDK2内のBdsConsole.cで定義されているので利用します.
変換した画像で画面を埋め尽くします.横端・縦端を描画する場合にはスクリーンをはみ出すので,書き込む範囲を予め計算しておきますが,横端を描画するために,描画するwidthを元画像より小さくしようとすると,何というか,水平同期の周期がズレる感じになって,うまく描画されません.1ピクセルずつ書けば良さそうですが,今回は諦めています.

ビルド

VS2012でビルドしました.下の記事を見てやってましたが,IntelliSense効かないっぽいのが悲しいです.
http://uefi.blogspot.jp/2012/06/how-to-set-up-edk2s-windows-hosted-uefi.html
実機で動かすバイナリをビルドするには,Conf/target.txtのACTIVE_PLATFORMをAppPkg/AppPkg.dscのように適切に変更しておきます.Nt32Pkg.dscでは動かなかったです.
また,実機がX64アーキテクチャしかサポートしていない場合には,VS2012の「VS2012 x64 Cross Tools コマンド プロンプト」からedk2setup.bat→buildでクロスコンパイルします.

動作

f:id:sosoru_m:20150819023004j:plain
実機動作.(ASRock Z87 Extreme 3)
1/2で出てくる高確率版を作って動作を確認しました.失敗した場合には,Windowsのブートに移ってくれるようです.
今日から1/65536版を入れてブートしますので,運悪く出てきたら追記します.

コード

EDK2のAppPkg/Mainを改変して作りました.
https://github.com/sosoru/sugunikese-on-uefi

その他

元ネタの記事には音もしたとあるので,ビープ音あたりで作れたら面白いのかなぁ.
むかし,SFCの「真・女神転生」やったときは,イケブクロあたりでやめてしまった気がします.RPGツクールの非公式リメイクでようやくエンディングを見ました.3Dダンジョン系はあんまり得意じゃないんだよなぁ.
この界隈のSFCのROM解析は進んでいて,バグを直すパッチが有志でリリースされてるの見たときはとても驚きました.

Railroaderの起動時の登録確認・認証を回避する(?)

http://traintrain.jp/info/close

traintrainさんが閉鎖されるらしい.昨日,Railroaderの記事を上げたばかりだというのに.
公式が対応してくれると思うけど,起動時登録確認を回避する方法をメモっておく

経緯

むかしむかし,東京メトロ干代田線のトンネルで携帯が使えなかった頃,移動中はRailroaderの登録確認が通らなくて辟易していた.
もちろん,サイトにはアカウントを登録していて,オンライン環境で使う分には全く問題は無いんだけど,移動中にレイアウト閲覧・編集が出来ないのはやはり痛かった.
そういうわけで,起動時認証を行うものの,エラー検出を無視するようにバイナリを改変して使っていた.最近は,移動中もずっと繋がるから要らないんだけど.

変更する箇所

Railroaderバージョン 4.0.57に適用

Address* Size* State* Old* New* Comment*
004BF9D0 6. Active JNZ 004BFC24 NOP if tree of show msgbox of invalid member ?
004BFA6A 6. Active JE 004BFB96 NOP if tree of show msgbox of api error ?

表の二カ所を改変すればいいらしい.

f:id:sosoru_m:20150801222309p:plain

MD5ハッシュ:
Original: 26170F6214887A3C0F7A958CE4D18C10
Modified: A1AC45AB6390EE8A8DFA8AB64651F9E7

この改変を行うと,サイト側にアカウント登録の確認を行いますが,エラー処理が行われないので,オンライン環境にあるかどうかに関わらずソフトを実行できます.

Railroaderで作成したレイアウトの解析+SVGファイルへの変換

昔々,突貫で作ったRailroaderパーサが出てきたので資料をメモっときます.

Railroaderとは

traintrain.jp
trantrainさんで公開されている鉄道模型用のレイアウトエディターです.
起動時に会員登録の確認を行うのが煩わしいですが,ソフト自体はとても使いやすいようで,ちらほらとユーザーを見かけます.
このソフトはレイアウトデータを,CSVのようなテキスト形式で保存してくれるので,プログラマーにとっても嬉しい限りです.

f:id:sosoru_m:20150731123609p:plain
こんな感じ

レイアウトデータについて

外部ツールでレールを描画する際に必要な所だけを説明します.
所々上げている画像はパーサ部分のC#コードです.

1~3行目はバージョンとレイアウトサイズが記述されているようです.
4行目以降のデータは計61列のcsvデータで,レールの位置を記述しています.

f:id:sosoru_m:20150731124304p:plain
0列目はレールが見える状態にあるかどうかです.Railroaderはレールを配置しようとしたときにidを発行しますが,操作をキャンセルしたり,設置した後レールを削除しようとすると,このフラグをFalseにして削除したように見せるようです.長時間編集したファイルが肥大化するのはこれが原因でしょう.
1列目はレールに振られたIDで,レール毎の接続情報で用いているようです.
2列目はレール種類の名前です.
3列目はレール種類のIDです.
5列目~7列目はレールの物理長さです.
9列目~37列目まではレール座標が記述されています.
f:id:sosoru_m:20150731125558p:plain
Combined~なレールって何だっけなぁ,レイアウトを外部ツールで描画する場合には気にする必要が無かったと思います.
ストレート・ポイント・カーブレールで使用しているパラメータが異なります.
ストレートレールの場合はCornerPosを結べば良さそうです.カーブレールの場合は27列目~37列目の情報を用いて弧を描きます.ポイントレールの場合はストレートとカーブの2つの情報を持っているので注意します.ポイントレールのカーブ側はCornerPos1→CornerPos3に固定されている(?)ようです.

f:id:sosoru_m:20150731132458p:plain
49列目~54列目まではレールの接続情報が記述されています.
NeighborRailxCntに何が格納されているかは失念.NeighborRailxに接続先のレールIDが格納されています.
55列目は高架レールかどうかです.
59列目はレールの色を示しています.
60列目は内部処理に使うIDのようです.

SVG形式への変換

SVGベクタ画像の一種でXMLで記述できます.fieldタグでHTMLに埋め込んだSVGファイルはjavascriptで要素を操作できます.
つまり,RailroaderのレイアウトデータをSVGに変換してHTMLに埋め込んでおけば,ネット上のデータとリアルタイムに連動できるので,模型の在線表示とかが可能なわけです.

f:id:sosoru_m:20150731152208p:plain
Railrodaerプレビュー

f:id:sosoru_m:20150731152226p:plain
WPFジオメトリに変換

f:id:sosoru_m:20150731152313p:plain
SVGに変換(Inkscapeで確認)

当時作っていたときはMVVMを勉強していたようで,WPFの描画パーツに分けた後,そのオブジェクト構造を利用してSVGファイルを作っていました.レイアウトの編集も企んでいたようですが,例外が出てて動いてないです.
下記はそのソースコードとバイナリです.バイナリの実行には.NET Framework 4.5が必要です.

sosoru/RailroaderConverter · GitHub