/*****************************************************************************
*
* FILE NAME:		IoSmpl.c
*
* DESCRIPTION:		RT-edge I/O sample service for INtime
*					I/O入出力サービスを想定したサンプルサービスを提供します
*
*
\*****************************************************************************/

#include <stdio.h>
#include <rt.h>
#include "edgeCode.h"		// RT-edge サービス共通定義/関数ヘッダ追加

/// <summary>
/// サービスインジケータ・プロパティタグ名
/// ------------------------------------------------------------
/// - サービスコンテナ名はECIで指定したName値を利用します。
/// - 変数の実体はメイン処理のソースファイルに宣言します。
/// - タグ名はEgInit()後に生成します。
/// ------------------------------------------------------------
/// </summary>
char IND_MYSERV_STATUS[EG_MAX_TAGNAME + 1] = { 0 };				// サービス起動
char IND_MYSERV_ERROR[EG_MAX_TAGNAME + 1] = { 0 };				// サービスエラー発生中
char IND_MYSERV_RUN[EG_MAX_TAGNAME + 1] = { 0 };				// サービス実行中
char IND_MYSERV_LIVE[EG_MAX_TAGNAME + 1] = { 0 };				// サービス実行カウンタ
char PROP_MYSERV_MODE[EG_MAX_TAGNAME + 1] = { 0 };				// サービス動作モード
char PROP_MYSERV_CYCLE[EG_MAX_TAGNAME + 1] = { 0 };				// サービス動作サイクル(ms)
char PROP_MYSERV_INPRIO[EG_MAX_TAGNAME + 1] = { 0 };			// Edgeシステムへの入力サービススレッドプライオリティ
char PROP_MYSERV_OUTPRIO[EG_MAX_TAGNAME + 1] = { 0 };			// Edgeシステムからの出力サービススレッドプライオリティ

/// <summary>
/// プロセスカタログ名
/// </summary>
char SERVICE_CATALOG_NAME[EG_MAX_CATALOGNAME] = { 0 };		// カタログ名

//////////////////////////////////////////////////////////////////////////////
/// 各種基本関数定義開始
///
/// <summary>
/// タグ名/カタログ名を生成する
/// [TODO] : 必要に応じてタグを追加
/// </summary>
void fnServiceTagNameCreate()
{
	// サービスインジケータ名
	sprintf(IND_MYSERV_STATUS, "SERVICE.%s.Status", ServiceRealName);		// サービス起動
	sprintf(IND_MYSERV_ERROR, "SERVICE.%s.Error", ServiceRealName);			// サービスエラー発生中
	sprintf(IND_MYSERV_RUN, "SERVICE.%s.Run", ServiceRealName);				// サービス実行中
	sprintf(IND_MYSERV_LIVE, "SERVICE.%s.Live", ServiceRealName);			// サービス実行カウンタ
	// サービスプロパティタグ名
	sprintf(PROP_MYSERV_MODE, "SERVICE.%s.Mode", ServiceRealName);			// サービス動作モード
	sprintf(PROP_MYSERV_CYCLE, "SERVICE.%s.Cycle", ServiceRealName);		// サービス動作サイクル(ms)
	sprintf(PROP_MYSERV_INPRIO, "SERVICE.%s.InPriority", ServiceRealName);	// Edgeシステムへの入力サービススレッドプライオリティ
	sprintf(PROP_MYSERV_OUTPRIO, "SERVICE.%s.OutPriority", ServiceRealName);// Edgeシステムからの出力サービススレッドプライオリティ
	// プロセスカタログ名
	sprintf(SERVICE_CATALOG_NAME, "EGSC%s", ServiceMailboxName);			// カタログ名

	//
	// [TODO] : 任意のタグを追加
	//
}
/// <summary>
/// Edgeメッセージハンドラ
/// [TODO] : 必要に応じてメッセージ・処理を追加
/// </summary>
/// <param name="sender">メッセージ送信元</param>
/// <param name="msgno">メッセージ番号</param>
/// <param name="param">メッセージパラメータ</param>
/// <returns>正常時０</returns>
int32_t MsgHandler(const char* sender, int32_t msgno, void *param)
{
	int32_t	result = 0;

	switch (msgno)
	{
	case EM_SERVICE_RUN:			// サービス開始指令
		result = StartService(&MyState);
		break;
	case EM_SERVICE_STOP:			// サービス終了指令
		result = KillService(&MyState);
		break;
	case EM_SERVICE_PAUSE:			// サービス一時停止指令
		result = PauseService(&MyState);
		break;
	case EM_SERVICE_UPDATE:			// サービスアップデート指令
		switch (MyState.Mode)
		{
		case MODE_AUTO:				// 自動サイクルモード時（サイクルスレッドが処理するので無処理）
			break;
		case MODE_SEMIAUTO:			// 半自動サイクルモード時（出力の更新を行う）
			KickOutput(&MyState);
			break;
		case MODE_MANUAL:			// 手動サイクルモード時（入出力共に更新を行う）
			KickInput(&MyState);
			KickOutput(&MyState);
			break;
		}
		break;
	//
	// [TODO] : 任意のメッセージ処理を追加
	//
	default:						//上記以外は抜ける
		break;
	}
	return result;
}
/// <summary>
/// Edgeシステム入出力自動サイクルスレッド
/// 必要が無ければ変更不要
/// </summary>
/// <param name="state"></param>
void ThreadAutoCycle(struct MYSTATE *state)
{
	RtSleepEx(1);						// 初回のトリガタイミングをカーネルティックにそろえる

	while (1)
	{
		++state->Live;					// サービス実行カウンタ増加
		GetProperty(state);				// サービスプロパティのリロード

		if (state->Run)					// サービス実行状態なら処理する
		{
			switch (state->Mode)		// サービス動作モードに応じて分岐
			{
			case MODE_AUTO:				// 自動サイクルモード時（入出力共に自動更新する）
				KickInput(state);
				KickOutput(state);
				break;
			case MODE_SEMIAUTO:			// 半自動サイクル時（入力のみ自動更新、出力はEM_SERVICE_UPDATEで指令）
				KickInput(state);
				break;
			case MODE_MANUAL:			// 手動サイクル時（入出力はEM_SERVICE_UPDATEで指令するので無処理）
				break;
			}
		}

		UpdateIndicator(state);			// サービスインジケータ更新
		RtSleepEx(state->Cycle);		// サイクル待ち
	}
}
/// <summary>
/// Edgeシステムへのデータ入力
/// [TODO] : 必要に応じて値を書き込むタグと値を指定
/// </summary>
/// <param name="state"></param>
void ThreadInService(struct MYSTATE *state)
{
	int32_t				result = 0;

	while (1)
	{
		WaitForRtSemaphore(state->hInTrigSem, 1, WAIT_FOREVER);			// 入力タイミングトリガ待ち
		//
		//[TODO] : 必要に応じて任意のEgTagへ値書き込みを実装
		//
		//例) 時間を格納
		// 以下例で示す ts はEDGE_TIME_STAMPのインスタンス 
		//result |= EgTagWrite(TAG_MYSERV_XXXX1, &ts.time, 8);			// 任意の値
		//result |= EgTagWrite(TAG_MYSERV_XXXX2, &ts.millisecond, 2);	// システム時刻格納(ms)

		state->Error = (result != EDGE_SUCCESS);	// エラーインジケータを更新
	}
}
/// <summary>
/// Edgeシステムからのデータ出力
/// [TODO] : 必要に応じて値を読み込むタグを指定
/// </summary>
/// <param name="state"></param>
void ThreadOutService(struct MYSTATE *state)
{
	while (1)
	{
		WaitForRtSemaphore(state->hOutTrigSem, 1, WAIT_FOREVER);		// 出力タイミングトリガ待ち
		//
		//[TODO] : 必要に応じて任意のEgTagから値読込を実装
		//
		//例) 時間を取得
		// 以下例で示す ts はEDGE_TIME_STAMPのインスタンス 
		//result |= EgTagRead(TAG_MYSERV_XXXX1, &ts.time, sizeof(ts.time));			// 任意の値
		//result |= EgTagRead(TAG_MYSERV_XXXX2, &ts.millisecond, , sizeof(ts.millisecond));	// システム時刻格納(ms)
	}
}
/// <summary>
/// 入力サービスを1回キックする
/// 必要が無ければ変更不要
/// </summary>
/// <param name="state"></param>
void KickInput(struct MYSTATE *state)
{
	ReleaseRtSemaphore(state->hInTrigSem, 1);
}

/// <summary>
/// 出力サービスを1回キックする
/// 必要が無ければ変更不要
/// </summary>
/// <param name="state"></param>
void KickOutput(struct MYSTATE *state)
{
	ReleaseRtSemaphore(state->hOutTrigSem, 1);
}
/// <summary>
/// サービスタグ生成 ※すでに生成されている場合は無視して継続します
/// [TODO] : 必要に応じて作成するタグを指定(タグの生成はXMLに定義し自動生成することを推奨)
/// </summary>
/// <param name="state"></param>
/// <returns>正常時０</returns>
int32_t CreateServiceTags(struct MYSTATE *state)
{
	int32_t		result = 0;

	// サービスタグ類の生成
	// [TODO] : 必要に応じて以下コードを修正
	//result |= EgTagCreateEx(TAG_MYSERV_XXX, EgVtUInt64, "", "sample tag");

	return EDGE_SUCCESS;
}
/// <summary>
/// サービスインジケータの更新 ※更新に失敗しても無視して継続します
/// 必要が無ければ変更不要
/// </summary>
/// <param name="state"></param>
/// <returns>正常時０</returns>
int32_t UpdateIndicator(struct MYSTATE *state)
{
	// インジケータタグはフレームワークによって自動生成済
	int32_t		result = 0;
	result |= EgTagWrite(IND_MYSERV_STATUS, &state->State, 1);		// サービス起動状態
	result |= EgTagWrite(IND_MYSERV_RUN, &state->Run, 1);			// サービス実行状態
	result |= EgTagWrite(IND_MYSERV_LIVE, &state->Live, 4);			// サービス実行カウンタ
	result |= EgTagWrite(IND_MYSERV_ERROR, &state->Error, 1);		// サービス異常状態
	return result;
}
/// <summary>
/// サービスプロパティの取得 ※取得できない時はデフォルト設定値を採用します
/// [TODO] : 必要に応じて設定項目の増減指定
/// </summary>
/// <param name="state"></param>
/// <returns>正常時０</returns>
int32_t GetProperty(struct MYSTATE *state)
{
	// プロパティタグはXML定義可能　無いときはデフォルト設定値を採用します
	int32_t		result;
	result = EgTagRead(PROP_MYSERV_MODE, &state->Mode, 1);				// サービス動作モードの取得
	if (result != EDGE_SUCCESS)	state->Mode = DEFAULT_MODE;				// デフォルト

	result = EgTagRead(PROP_MYSERV_CYCLE, &state->Cycle, 4);			// サービス処理サイクルの取得
	if (result != EDGE_SUCCESS)	state->Cycle = DEFAULT_CYCLE;			// デフォルト

	result = EgTagRead(PROP_MYSERV_INPRIO, &state->InPriority, 1);		// 入力サービススレッドのプライオリティ指定
	if (result != EDGE_SUCCESS)	state->InPriority = DEFAULT_INPRIO;		// デフォルト

	result = EgTagRead(PROP_MYSERV_OUTPRIO, &state->OutPriority, 1);	// 出力サービススレッドのプライオリティ指定
	if (result != EDGE_SUCCESS)	state->OutPriority = DEFAULT_OUTPRIO;	// デフォルト

	//
	// [TODO] : 任意のサービスプロパティを指定
	//
	return EDGE_SUCCESS;
}
/// <summary>
/// サービス開始 ※入出力可能になります
/// 必要が無ければ変更不要
/// </summary>
/// <returns>正常時０</returns>
int32_t StartService(struct MYSTATE *state)
{
	state->Run = true;				// サービス実行
	UpdateIndicator(state);			// サービスインジケータ更新
	return 0;
}
/// <summary>
/// サービス一時停止 ※入出力されなくなります
/// 必要が無ければ変更不要
/// </summary>
/// <returns>正常時０</returns>
int32_t PauseService(struct MYSTATE *state)
{
	state->Run = false;				// サービス一時停止
	UpdateIndicator(state);			// サービスインジケータ更新
	return 0;
}
/// <summary>
/// サービスの初期化
/// </summary>
/// <returns>正常時０</returns>
int32_t InitService(struct MYSTATE *state)
{
	int32_t		result;
	EDGECONFIG	config = EDGE_CONFIG_DEFAULT;
	RTHANDLE	hThread;

	printf("[LOG] : Start Sample Service\n");
	config.fpUserMessHdlFunc = MsgHandler;						// Edgeメッセージユーザハンドラの指定

	result = EgInit(&config);									// Edgeフレームワークの初期化
	if (result != EDGE_SUCCESS)
		WARN(result);

	fnServiceTagNameCreate();	// サービスコンテナ名付タグ名を生成する

	result = GetProperty(state);								// サービスプロパティのロード処理
	if (result != EDGE_SUCCESS)
		WARN(result);

	result = CreateServiceTags(state);							// サービスインジケータの生成
	if (result != EDGE_SUCCESS)
		WARN(result);

	state->hInTrigSem = CreateRtSemaphore(0, 10, FIFO_QUEUING);	// 入力サービススレッド用トリガセマフォ
	state->hOutTrigSem = CreateRtSemaphore(0, 10, FIFO_QUEUING);// 出力サービススレッド用トリガセマフォ

	SetRtProcessMaxPriority(0, 0);														// プロセス最上位プライオリティ制限解除

	hThread = CreateRtThread(state->OutPriority, ThreadOutService, 0x2000, state);		// 出力サービススレッドの生成
	CatalogRtHandle(0, hThread, "OutServ");

	hThread = CreateRtThread(state->InPriority, ThreadInService, 0x2000, state);		// 入力サービススレッドの生成
	CatalogRtHandle(0, hThread, "InServ");

	uint8_t trigprio = (state->OutPriority < state->InPriority) ? state->OutPriority : state->InPriority;	// 高いほうのプライオリティを取得
	hThread = CreateRtThread(trigprio, ThreadAutoCycle, 0x2000, state);					// サービスサイクルスレッドの生成
	CatalogRtHandle(0, hThread, "AutoCycle");

	RTHANDLE hRoot = GetRtThreadHandles(ROOT_PROCESS);			// プロセス名をカタログ
	RTHANDLE hThisProc = GetRtThreadHandles(THIS_PROCESS);
	UncatalogRtHandle(hRoot, SERVICE_CATALOG_NAME);
	CatalogRtHandle(hRoot, hThisProc, SERVICE_CATALOG_NAME);
	SetLastRtError(0);

	state->State = SERVICE_UP;									// サービスアップ
	UpdateIndicator(state);										// サービスインジケータの更新

	return result;
}

/// <summary>
/// サービス削除（プロセス停止）
/// </summary>
/// <returns>この関数から戻ることはありません</returns>
int32_t KillService(struct MYSTATE *state)
{
	PauseService(state);				// サービス一時停止

	state->State = SERVICE_DOWN;		// サービスダウン
	UpdateIndicator(state);				// サービスインジケータ更新

	TerminateRtProcess(0, 1, 1000);		// 自プロセスに終了を要求
										// ここではEgFinalizeできない。戻ってこなくなります。
	printf("[LOG] : End %s Service\n", ServiceRealName);
	return 0;
}
/// <summary>
/// ワーニングメッセージ
/// </summary>
/// <param name="position">問題発生個所 ※__LINE__を指定</param>
/// <param name="reason">失敗理由コード</param>
void WarningMsg(int32_t position, int32_t reason)
{
	printf("%s service warning at line(%d) ,reason 0x%08x\n", ServiceRealName, position, reason);
}