#include <clip.h>
/*---------------------------------------------------------------------------*/
/* ■変更履歴
 * * Fri Nov 28 00:00:00 JST 2014 Naoyuki Sawa
 * - パルスモーターの直接制御を追加したことに伴い、シーケンサの命令セット,命令コード,命令コード定義用マクロを変更しました。
 *   cliprudc.*,clipruds.cの上記の変更を反映するために、当ツールもリビルドしました。
 *   当ツールのソースは、バージョン定義以外は変更していません。
 * * Fri Dec 12 14:26:57 JST 2014 Naoyuki Sawa
 * - 条件分岐'if on','if off'を追加したことに伴い、シーケンサの命令セット,命令コード,命令コード定義用マクロを変更しました。
 *   cliprudc.*,clipruds.cの上記の変更を反映するために、当ツールもリビルドしました。
 *   当ツールのソースは、バージョン定義以外は変更していません。
 * * Mon Apr 13 21:23:45 JST 2015 Naoyuki Sawa
 * - dRudSeqC.exeの出力形式を2015/02/24に大幅に変更したことに対応して、当ツールも変更後の出力形式を扱うように変更しました。
 *   主な変更点は、以下の通りです。
 *   ・<Data>要素のデータの中に、インデクステーブルも含めるようにしました。(dRudSeqC.exeのバイナリ出力形式と同じ)
 *   ・'-x'オプションで指定する値を、データ内のハーフワードインデクスでなく、'RudSeqNo_*'に対応する連番指定に変更しました。
 *   ・入力するxmlファイルの<Data>要素を、C言語形式でなく、Base64形式に変更しました。
 *     以下の方法で変換してください。尚、'-c76'は読みやすさのためだけであり、指定しなくても構いません。
 *     ＞ dbase64enc.exe -c76 RudDef.bin
 * * Sat Aug 01 21:04:32 JST 2015 Naoyuki Sawa
 * - clipruds.cのRudSeqCh_Play()を変更したことに伴い、当ツールもリビルドしました。
 *   当ツールのソースコードには、変更は有りません。
 * - 今回のRudSeqCh_Play()による、当ツールの出力結果への影響は、動作が1ミリ秒早く進むようになった事だけです。
 *   実用上は変更前とほとんど違いは無いのですが、厳密なシミュレーション結果とするために当ツールもリビルドする事にしました。
 * * Sun Feb 21 21:02:01 JST 2016 Naoyuki Sawa
 * - 「Boehm GC」をマルチスレッド対応にした事,及び,それに伴ってclipbpe.cモジュールを修正した事に伴い、当ツールもプロジェクト設定の変更を行ってリビルドしました。
 *   詳細は、/clip/tool/gc/README.TXT と /clip/clipbpe.c の2015/11/03のコメントを参照して下さい。
 * - libclipのライブラリ化の伴い、以下の変更を行いました。
 *   ・extclip.hとextclip.cを削除しました。extclip.hとextclip.cはlibclipに含まれるようになったからです。
 *   ・"CLiP Files"を削除しました。CLiPライブラリの各モジュールはlibclipに含まれるようになったからです。
 *   当ツールの動作には変更有りません。
 */
#define VERSION "20160221"	/* バージョン定義 */
/*---------------------------------------------------------------------------*/
ST_RudCtlInfo stRudCtlInfo;
int iChNum;
int iDataLen;
uint8_t TBL_Data[1000000/*調整可*/];
/*---------------------------------------------------------------------------*/
void RudCtl_fnMove(int iRudCtlCh, const ST_RudCtlAct* pRudCtlAct) {
	/** no job **/
}
/*---------------------------------------------------------------------------*/
void RudSeq_fnUser(int iRudSeqCh, int no) {
	/** no job **/
}
/*---------------------------------------------------------------------------*/
int getValue(const char* p) {
	return p ? ShuntingYard_Eval(p) : 0;
}
/*---------------------------------------------------------------------------*/
void loadInfo(TiXmlElement* pRoot) {
	TiXmlElement* pInfo = TiXmlNode_FirstChildElement(TiXmlElement_super(pRoot), "Info");
	if(!pInfo) { DIE(); }
	{
		stRudCtlInfo.fnMove	= RudCtl_fnMove;
		stRudCtlInfo.fCLK	= getValue(TiXmlElement_Attribute(pInfo, "fCLK"));
		{
			TiXmlElement* pCh = TiXmlNode_FirstChildElement(TiXmlElement_super(pInfo), "Ch");
			if(!pCh) { DIE(); }
			do {
				if(iChNum >= ARRAY_SIZE(stRudCtlInfo.TBL_RudCtlChInfo)) { DIE(); }
				stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Sns	= getValue(TiXmlElement_Attribute(pCh, "Sns"));
				stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Bac	= getValue(TiXmlElement_Attribute(pCh, "Bac"));
				{
					int iDir = 0;
					TiXmlElement* pDir = TiXmlNode_FirstChildElement(TiXmlElement_super(pCh), "Dir");
					if(!pDir) { DIE(); }
					do {
						if(iDir >= ARRAY_SIZE(stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir)) { DIE(); }
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].PosLim	= getValue(TiXmlElement_Attribute(pDir, "PosLim"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].PosFix	= getValue(TiXmlElement_Attribute(pDir, "PosFix"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].PosSns	= getValue(TiXmlElement_Attribute(pDir, "PosSns"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].n	= getValue(TiXmlElement_Attribute(pDir, "n"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].n1	= getValue(TiXmlElement_Attribute(pDir, "n1"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].r1	= getValue(TiXmlElement_Attribute(pDir, "r1"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].s1	= getValue(TiXmlElement_Attribute(pDir, "s1"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].n2	= getValue(TiXmlElement_Attribute(pDir, "n2"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].r2	= getValue(TiXmlElement_Attribute(pDir, "r2"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].s2	= getValue(TiXmlElement_Attribute(pDir, "s2"));
						stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir[iDir].StpTim	= getValue(TiXmlElement_Attribute(pDir, "StpTim"));
						pDir = TiXmlNode_NextSiblingElement(TiXmlElement_super(pDir), "Dir");
						iDir++;
					} while(pDir);
					if(iDir != ARRAY_SIZE(stRudCtlInfo.TBL_RudCtlChInfo[iChNum].Dir)) { DIE(); }
				}
				pCh = TiXmlNode_NextSiblingElement(TiXmlElement_super(pCh), "Ch");
				iChNum++;
			} while(pCh);
		}
	}
}
/*---------------------------------------------------------------------------*/
void loadData(TiXmlElement* pRoot) {
	TiXmlElement* pData = TiXmlNode_FirstChildElement(TiXmlElement_super(pRoot), "Data");
	if(!pData) { DIE(); }
	{
		TiXmlNode* pNode = TiXmlNode_FirstChild(TiXmlElement_super(pData), NULL);
		if(!pNode) { DIE(); }
		{
			TiXmlText* pText = TiXmlNode_ToText(pNode);
			if(!pText) { DIE(); }
			{
				const char* p = TiXmlNode_Value(TiXmlText_super(pText));
				p = strtrim(p, 3, NULL);	/* 文字列内にある全ての改行と空白文字を除去する。base64_decode()は改行や空白文字を除去しないのでこの処理は必須である。 */
				iDataLen = base64_decode(TBL_Data, sizeof TBL_Data, p);
			}
		}
	}
}
/*---------------------------------------------------------------------------*/
void LoadXml(const char* pFileName) {
	TiXmlDocument* pDoc = TiXmlDocument_new(NULL);
	TiXmlDocument_LoadFile(pDoc, pFileName);
	{
		TiXmlElement* pRoot = TiXmlNode_FirstChildElement(TiXmlDocument_super(pDoc), "Root");
		if(!pRoot) { DIE(); }
		loadInfo(pRoot);
		loadData(pRoot);
	}
}
/*---------------------------------------------------------------------------*/
int getSns(int iCh) {
	int PosSns_Min, PosSns_Max;
	       if(RudCtlCh_GetPosSns(iCh, 0) < RudCtlCh_GetPosSns(iCh, 1)) {
		PosSns_Min = RudCtlCh_GetPosSns(iCh, 0);
		PosSns_Max = RudCtlCh_GetPosSns(iCh, 1);
	} else if(RudCtlCh_GetPosSns(iCh, 0) > RudCtlCh_GetPosSns(iCh, 1)) {
		PosSns_Min = RudCtlCh_GetPosSns(iCh, 1);
		PosSns_Max = RudCtlCh_GetPosSns(iCh, 0);
	} else if(RudCtlCh_GetPosSns(iCh, 0) < 0) {
		PosSns_Min = RudCtlCh_GetPosSns(iCh, 0);
		PosSns_Max = 0;
	} else {
		PosSns_Min = 0;
		PosSns_Max = RudCtlCh_GetPosSns(iCh, 0);
	}
	return ((RudCtlCh_GetPos(iCh) >= PosSns_Min) &&
		(RudCtlCh_GetPos(iCh) <= PosSns_Max)) ? RudCtlCh_GetInfo(iCh)->Sns : 0;
}
/*---------------------------------------------------------------------------*/
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dRudSeqS - パルスモーターの台形制御シーケンサシミュレータ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2014-2016 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dRudSeqS [options] filename.xml\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -h     使い方を表示します。\n");
	fprintf(stderr, "  -c数値 チャネル番号       指定しなければ、チャネル=0とする。\n");
	fprintf(stderr, "  -p数値 初期位置           指定しなければ、初期位置=0とする。\n");
	fprintf(stderr, "  -x数値 実行するデータ番号 指定しなければ、何もせず終了する。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  dRudSeqS -c2 -p-10 -x30 sample.xml\n");
	fprintf(stderr, "  sample.xmlを読込み、チャネル=2,初期位置=-10で、データ[30]を実行する。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OUTPUT:\n");
	fprintf(stderr, "  「>」で始まる行の出力形式は、「>時間 位置 分周比 方向」です。\n");
	fprintf(stderr, "  「>」で始まる行以外は、デバッグ表示なので、無視してください。\n");
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
int optCh;		/* チャネル番号 */
int optPos;		/* 初期位置 */
int optRudSeqNo;	/* 実行するデータ番号 */
/*---------------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
	int opt, cnt;
	/* コマンドラインオプションを処理する。 */
	opterr = 0; /* オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。 */
	while((opt = getopt(argc, argv, "hc:p:x:")) != -1) {
		switch(opt) {
		default:
		case 'h': usage();
		case 'c': optCh       = atoi(optarg); break;
		case 'p': optPos      = atoi(optarg); break;
		case 'x': optRudSeqNo = atoi(optarg); break;
		}
	}
	if(optind != (argc - 1)) { usage(); }
	/* XMLファイルを読み込む。 */
	LoadXml(argv[optind]);
	if((unsigned)optCh >= (unsigned)iChNum) { die("-cオプションの引数が不正です。"); }
	/* 実行するデータ番号が指定されていたら… */
	if(optRudSeqNo) {
		int numRudSeqNo, ofsRudSeqNo;
		numRudSeqNo = GET_LEWORD(&TBL_Data[0]);
		if((unsigned)optRudSeqNo >= (unsigned)numRudSeqNo) { die("-xオプションの引数が不正です。"); }
		ofsRudSeqNo = GET_LEWORD(&TBL_Data[optRudSeqNo * 4]) * sizeof(uint16_t);
		ofsRudSeqNo +=                           numRudSeqNo * sizeof(uint32_t);
		/* コントローラとシーケンサを初期化する。 */
		RudCtl_Init(&stRudCtlInfo);
		RudSeq_Init(RudSeq_fnUser);
		/* コントローラの初期状態(位置とセンサ)を設定する。 */
		if(RudCtlCh_GetPosLim(optCh, 0) < RudCtlCh_GetPosLim(optCh, 1)) {
			if((optPos < RudCtlCh_GetPosLim(optCh, 0)) ||
			   (optPos > RudCtlCh_GetPosLim(optCh, 1))) { die("-pオプションの引数が不正です。"); }
		} else {
			if((optPos < RudCtlCh_GetPosLim(optCh, 1)) ||
			   (optPos > RudCtlCh_GetPosLim(optCh, 0))) { die("-pオプションの引数が不正です。"); }
		}
		RudCtlCh_Get(optCh)->Pos = optPos;
		RudCtl_Exec(getSns(optCh));	/* センサを反映するために一回実行する */
		/* データを実行する。 */
		RudSeqCh_Play(optCh, (uint16_t*)&TBL_Data[ofsRudSeqNo]);
		cnt = 0;
		do {
			RudCtl_Exec(getSns(optCh));
			RudSeq_Exec();
			printf(">%d %d %d %d\n",
				cnt++,				/* 時間 */
				RudCtlCh_GetPos(optCh),		/* 位置 */
				RudCtlCh_GetSpd(optCh),		/* 分周比 */
				RudCtlCh_Get(optCh)->Act.d);	/* 方向 */
		} while(RudCtlCh_GetSpd(optCh) ||		/* モーターが動作中 */
			RudCtlCh_GetStpTim(optCh) ||		/* モーターが停止励磁中 */
		//	RudCtlCh_Get(optCh)->ReqEvt1.Cond ||	/* 実行中動作要求(イベント実行要求)有り */	⇒この継続条件は不要。この継続条件を含めてしまうと、イベント実行要求がセンサエッジでセンサエッジ発生前に即時実行要求が完了した場合に、終わらなくなる。
			RudCtlCh_Get(optCh)->ReqImm2.Cond ||	/* 停止後動作要求(即時実行要求)有り */
		//	RudCtlCh_Get(optCh)->ReqEvt2.Cond ||	/* 停止後動作要求(イベント実行要求)有り */	⇒この継続条件は不要。この継続条件を含めてしまうと、イベント実行要求がセンサエッジでセンサエッジ発生前に即時実行要求が完了した場合に、終わらなくなる。
			stRudSeq.TBL_RudSeqCh[optCh].pInst);	/* シーケンサーが動作中 */
	}
	return EXIT_SUCCESS;
}
