#include <clip.h>
/*---------------------------------------------------------------------------*/
#define VERSION "20140614"	/* バージョン定義 */
/*---------------------------------------------------------------------------*/
ST_RudCtlInfo stRudCtlInfo;
int iChNum;
int iDataLen;
uint16_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(); }
			{
				char* p = strtok(strdup(TiXmlNode_Value(TiXmlText_super(pText))), ", ");
				while(p) {
					if(iDataLen >= ARRAY_SIZE(TBL_Data)) { DIE(); }
					TBL_Data[iDataLen++] = strtol(p, &p, 0);
					if(*p) { DIE(); }
					p = strtok(NULL, ", ");
				}
			}
		}
	}
}
/*---------------------------------------------------------------------------*/
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 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 -x300 sample.xml\n");
	fprintf(stderr, "  sample.xmlを読込み、チャネル(2),初期位置(-10)で、データ(300〜)を実行する。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OUTPUT:\n");
	fprintf(stderr, "  「>」で始まる行の出力形式は、「>時間 位置 分周比 方向」です。\n");
	fprintf(stderr, "  「>」で始まる行以外は、デバッグ表示なので、無視してください。\n");
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
int optCh;	/* チャネル番号 */
int optPos;	/* 初期位置 */
int optData;	/* データ実行インデクス */
/*---------------------------------------------------------------------------*/
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': optData = atoi(optarg); break;
		}
	}
	if(optind != (argc - 1)) { usage(); }
	/* XMLファイルを読み込む。 */
	LoadXml(argv[optind]);
	if((unsigned)optCh   >= (unsigned)iChNum  ) { die("-cオプションの引数が不正です。"); }
	if((unsigned)optData >= (unsigned)iDataLen) { die("-xオプションの引数が不正です。"); }
	/* データ実行インデクスが指定されていたら… */
	if(optData) {
		/* コントローラとシーケンサを初期化する。 */
		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, &TBL_Data[optData]);
		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;
}
