/********************************************************************************
*		ソースファイル名：CollectSETTING2.cpp										
*		用            途：設定２データ収集クラス												
*		備            考：														
*		履            歴：														
********************************************************************************/
/****************************
*   コンパイル環境定数	    *
****************************/
// CollectSETTING2.cpp : 実装ファイル
//

#include "stdafx.h"
/****************************
*   インクルードファイル    *
****************************/
#include "CollectSetting2.h"
#include "WMes.h"
#include "DailyLogManagerS.h"
#include "CsvFile.h"
/****************************
*        マクロ定義			*
****************************/
#define	THREAD_WAIT_MS		1000		// スレッド周期[ms]

//***** ガード条件
#define GUARD_RETURN( expr, ret )		{ if( !(expr) )	return (ret) ;		}
#define GUARD_RETURN_VOID( expr )		{ if( !(expr) )	return ;			}
#define GUARD_RETURN_FALSE( expr )		{ if( !(expr) )	return FALSE ;		}
#define GUARD_RETURN_NULL( expr, T )	{ if( !(expr) )	return (T *)NULL ;	}

/****************************
*        構造体定義		    *
****************************/
/****************************
*        クラス定義		    *
****************************/
/****************************
*         関数 宣言		    *
****************************/
/****************************
*		　変数　宣言	    *
****************************/
// CCollectSETTING2
/********************************************************************************
*	関数名：CCollectSETTING2														
*	用　途：コンストラクタ														
*	引き数：																	
*	戻り値：																	
*	備　考：																	
********************************************************************************/
CCollectSETTING2::CCollectSETTING2()
{
	m_bInitialize		= FALSE ;
	m_pSETTING2Queue	= NULL ;
	m_bInputPath		= FALSE ;

	m_nFileMax			= 1000;
	m_nFileCnt			= m_nFileMax + 1;

	ClearData() ;
}

/********************************************************************************
*	関数名：~CCollectSETTING2														
*	用　途：デストラクタ														
*	引き数：																	
*	戻り値：																	
*	備　考：																	
********************************************************************************/
CCollectSETTING2::~CCollectSETTING2()
{
	ClearData() ;
}

/********************************************************************************
*	関数名：GetInstance
*	用　途：インスタンス取得（初回は生成）
*	引き数：
*	戻り値：
*	備　考
********************************************************************************/
CCollectSETTING2& CCollectSETTING2::GetInstance(void)
{
	static CCollectSETTING2 Singleton;
	return Singleton;
}

// CCollectSETTING2 メッセージ ハンドラ
/********************************************************************************
*	関数名：Initialize															
*	用　途：初期化																
*	引き数：																	
*	戻り値：																	
*	備　考：																	
********************************************************************************/
BOOL CCollectSETTING2::Initialize( CRingQueue* pSETTING2Queue )
{
	m_tpParam			= &CParameterManager::GetInstance().RefParamInit()->taCollect[COLLECT_SETTING2] ;
	m_strInputPath		= m_tpParam->szDataPath ;
	m_pSETTING2Queue	= pSETTING2Queue ;
	m_nFileMax			= m_tpParam->uFilesInFolder;
	m_nFileCnt			= m_nFileMax + 1;

	m_bInputPath = TRUE ;
	if( !CheckInputPath( m_strInputPath ) ){
		CDailyLogManagerS::GetInstance().PrintLog( LOG_ERROR, _T("[設定2データ]データ入力パス異常：Path = %s"), m_strInputPath ) ;
		m_bInputPath = FALSE ;
	}

	if( m_pSETTING2Queue == NULL){
		CDailyLogManagerS::GetInstance().PrintLog( LOG_ERROR, _T("[設定2データ]データ格納キュー異常") ) ;
		return FALSE ;
	}

	// 入力元パス有無メッセージ送信
	PostToView( WM_INPUT_PATH, LOG_SETTING2, m_bInputPath ) ;

	// データ初期化
	ClearData() ;

	// 初期化完了なら自分でスレッド開始
	Begin() ;

	m_bInitialize = TRUE ;

	CDailyLogManagerS::GetInstance().PrintLog( LOG_SETTING2, _T("[設定2データ]データ収集開始：Path = %s"), m_strInputPath ) ;

	return m_bInitialize ;
}

/********************************************************************************
*	関数名：ClearData															
*	用　途：データクリア															
*	引き数：																	
*	戻り値：																	
*	備　考：																	
********************************************************************************/
void CCollectSETTING2::ClearData()
{
	// 設定２データクリア
	ZeroMemory( &m_tSETTING2Data , sizeof( SETTING2_DATA ) ) ;
}

/*******************************************************************************
*	関　数　名：Wait
*	用　　　途：スレッド終了指示待ち
*	引　き　数：待ち時間[ms]
*	戻　り　値：スレッド終了指示あり(1) / なし(0) / エラー(-1)
*	備　　　考：通常エラーは通常ありえない
********************************************************************************/
INT32 CCollectSETTING2::Wait( UINT uWaitTime_ms )
{
	INT32	nRet ;
	DWORD	dwWaitResult ;

	//***** スレッド終了イベントハンドルを待機する
	dwWaitResult = ::WaitForSingleObject( CThread::m_hClose, (DWORD)uWaitTime_ms ) ;

	switch( dwWaitResult )
	{
		case WAIT_OBJECT_0:		nRet = (INT32)TRUE ;	break ;
		case WAIT_TIMEOUT:		nRet = (INT32)FALSE ;	break ;
		default:				nRet = -1 ;				break ;
	}

	return nRet ;
}

/********************************************************************************
*	関数名：Main															
*	用　途：スレッドメイン																
*	引き数：																	
*	戻り値：																	
*	備　考：																	
********************************************************************************/
BOOL CCollectSETTING2::Main()
{
	CString	strFilePath, strMovePath, strFileName, strTmp, strNewPath;
	BYTE	byNgFlag ;
	BOOL	bInputPath ;

	while( Wait(0) == 0 ){
		// 入力元パス確認
		bInputPath = CheckInputPath( m_strInputPath ) ;

		// 入力元パス有無に変更あれば保持
		if( m_bInputPath != bInputPath ){
			m_bInputPath = bInputPath ;

			// 入力元パス有無メッセージ送信
			PostToView( WM_INPUT_PATH, LOG_SETTING2, m_bInputPath ) ;
		}

		// 入力元にファイルあれば解析開始
		if( FindSETTING2File( m_strInputPath, &strFilePath ) ){
			// データ初期化
			ClearData() ;

			// 設定２データファイル解析
			byNgFlag = 0x00 ;
			if( AnalyzeSETTING2File( strFilePath ) )
			{
				CDailyLogManagerS::GetInstance().PrintLog( LOG_SETTING2, _T("[設定2データ]データファイル検知： File = %s"), strFilePath ) ;

				// 設定２データキューへ格納
				m_pSETTING2Queue->Push( (PVOID)&m_tSETTING2Data ) ;
			}

			// 解析完了したファイルはリネーム or 移動
			if( !m_tpParam->bFileMove ){
				//*** リネーム
				strMovePath.Format( _T("%s_"), strFilePath ) ;
			}else{
				//*** 移動
				// 移動先パス
				strMovePath = m_tpParam->szMovePath ;

				// 移動先あるかチェック(無ければ作成)
				::CreateDirectory( strMovePath, NULL ) ;

				// ファイル名の取得
				strFileName = strFilePath.Right( strFilePath.GetLength() - strFilePath.ReverseFind(_T('\\')) - 1 );

				// 上位階層フォルダコピー
				if( m_tpParam->nUpFolderCopy > 0 )
				{
					strTmp = strFilePath.Left( strFilePath.ReverseFind(_T('\\')) );
					UpFolderCopy( m_tpParam->nUpFolderCopy - 1, strTmp, &strMovePath ) ;
				}

				// 新規にフォルダを作成する必要があるか？
				if (IsNewFolder())
				{
					MakeFolderName(&m_strFolder);
					m_nFileCnt = 0;

					strNewPath = strMovePath + _T("\\") + m_strFolder;
					::CreateDirectory(strNewPath, NULL);
				}

				// 最後にファイル名つけてファイルパスの作成
				strMovePath += _T("\\") + m_strFolder + _T("\\") + strFileName;
			}
			MoveFileEx( strFilePath, strMovePath, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH ) ;

			CDailyLogManagerS::GetInstance().PrintLog( LOG_SETTING2, _T("[設定2データ]データファイル解析完了： MoveFile = %s"), strMovePath ) ;

			m_nFileCnt++;
		}

		// スレッド終了指示あれば終了
		if( Wait(0) != 0 ) break ;

		//***** 休憩
		::Sleep( THREAD_WAIT_MS ) ;
	}

	return TRUE ;
}

/*******************************************************************************
*	関　数　名：UpFolderCopy
*	用　　　途：上位フォルダコピー
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
void CCollectSETTING2::UpFolderCopy( INT32 nUpCount, CString strSrcPath, CString* pstrDstPath )
{
	CString strTmp ;

	// 最上位指定階層まで再帰
	if( nUpCount > 0 ){
		strTmp = strSrcPath.Left( strSrcPath.ReverseFind(_T('\\')) );
		UpFolderCopy( nUpCount - 1, strTmp, pstrDstPath ) ;
	}

	// 右から\\までの値をコピー
	strTmp = strSrcPath.Right( strSrcPath.GetLength() - strSrcPath.ReverseFind(_T('\\')) - 1 );
	*pstrDstPath = *pstrDstPath + _T("\\") + strTmp ;

	// フォルダ存在しているかチェック(無ければ作成)
	::CreateDirectory( *pstrDstPath, NULL ) ;
}

/********************************************************************************
*	関数名：FindSETTING2File													
*	用　途：設定２データファイル検索												
*	引き数：ファイルパス格納先														
*	戻り値：															
*	備　考：1つでも該当ファイルあれば終了									
********************************************************************************/
BOOL CCollectSETTING2::FindSETTING2File( CString strInputPath, CString* pstrFilePath )
{
	BOOL		bRet = FALSE ;
	BOOL		bFind ;
	CFileFind	Find ;
	CString		strFind, strName, strFilePath, strTmpPath ;

	// 入力元フォルダ内検索
	strFind.Format( _T("%s\\*"), strInputPath ) ;
	bFind = Find.FindFile( strFind ) ;

	// ファイル見つかったら設定２データファイルかチェック
	while( bFind ){
		// 次のファイルがあるか事前に確認しておく
		bFind = Find.FindNextFileA() ;

		if( Find.IsDots()	   ) continue ;

		// フォルダがあった場合は再帰処理
		if( Find.IsDirectory() ) {
			bRet = FindSETTING2File( Find.GetFilePath(), pstrFilePath ) ;

			// ファイル見つかっていたら終了
			if( bRet ) break ;
		}

		// ファイル名取得
		strName = Find.GetFileName() ;

		// 拡張子がcsv以外は無視
		if( strName.Right(3) != _T("csv") ) continue ;

		// 設定２用ファイル以外は無視
		if( strName.Find(m_tpParam->szFileName) < 0 ) continue ;

		//*** 設定２データファイル検出

		// 一旦待機(もしかしたら書き込み途中かもしれない)
		Sleep(100) ;

		// ファイルパスを取得
		*pstrFilePath = Find.GetFilePath() ;

		// 終了
		bRet = TRUE ;
		break ;
	}

	return bRet ;
}

/********************************************************************************
*	関数名：AnalyzeSETTING2File													
*	用　途：設定２データファイル解析												
*	引き数：ファイルパス														
*	戻り値：															
*	備　考：									
********************************************************************************/
BOOL CCollectSETTING2::AnalyzeSETTING2File( CString strFilePath )
{
	CCsvFile	CsvFile;
	CCsv		csv;
	INT32		nColOffset ;
	CString		strTmp ;
	PINT32		pnColNo = m_tpParam->pnCsvColNo ;

	// CSVファイル読み込み
	CsvFile.SetFileName( strFilePath ) ;

	// CSV読み込み失敗なら終了
	if( !CsvFile.Load() ) return FALSE ;

	// データは最終行取得
	csv	= CsvFile.GetAt(CsvFile.GetCount() - 1);

	strncpy_s(m_tSETTING2Data.szWorkID, _countof(m_tSETTING2Data.szWorkID), csv.GetString(pnColNo[SETTING2_WORKID]), _countof(m_tSETTING2Data.szWorkID) - 1);		// ワークID

	// 計測日時は'_'を削除する
	strTmp = csv.GetString( pnColNo[SETTING2_DATETIME] ) ;
	strTmp.Remove( _T('_') ) ;
	strncpy_s(m_tSETTING2Data.szDateTime, _countof(m_tSETTING2Data.szDateTime), strTmp, _countof(m_tSETTING2Data.szDateTime) - 1);

	m_tSETTING2Data.nOpNo	 = csv.GetInt32( pnColNo[SETTING2_OPNO	 ] ) ;		// OP番号
	m_tSETTING2Data.nMesItem	 = csv.GetInt32( pnColNo[SETTING2_MESITEM ] ) ;		// 計測項目
	m_tSETTING2Data.nNgBit 	 = csv.GetInt32( pnColNo[SETTING2_NGBIT	 ] ) ;		// NGビット
	m_tSETTING2Data.nResultID = csv.GetInt32( pnColNo[SETTING2_RESULTID] ) ;		// 結果ID
	m_tSETTING2Data.nToolNo = csv.GetInt(pnColNo[SETTING2_TOOLNO]); //	工具番号

	strncpy_s(m_tSETTING2Data.szMesItem,	_countof(m_tSETTING2Data.szMesItem),	csv.GetString(pnColNo[SETTING2_MESITEM_NAME]),	_countof(m_tSETTING2Data.szMesItem)  - 1);		// 計測項目名称
	strncpy_s(m_tSETTING2Data.szDataItem,	_countof(m_tSETTING2Data.szDataItem),	csv.GetString(pnColNo[SETTING2_DATAITEM]),		_countof(m_tSETTING2Data.szDataItem) - 1);		// データ項目
	strncpy_s(m_tSETTING2Data.szCncNo,		_countof(m_tSETTING2Data.szCncNo),		csv.GetString(pnColNo[SETTING2_CNC_NO]),		_countof(m_tSETTING2Data.szCncNo)	 - 1);		// CNC No
	strncpy_s(m_tSETTING2Data.szIpAddress,  _countof(m_tSETTING2Data.szIpAddress), csv.GetString(pnColNo[SETTING2_IPADDRESS]),		_countof(m_tSETTING2Data.szIpAddress) - 1);		// IpAddress(加工機)
	strncpy_s(m_tSETTING2Data.szModelName,  _countof(m_tSETTING2Data.szModelName), csv.GetString(pnColNo[SETTING2_MODELNAME]),		_countof(m_tSETTING2Data.szModelName) - 1);		// ModelName(加工機名)
	strncpy_s(m_tSETTING2Data.szFeatureType, _countof(m_tSETTING2Data.szFeatureType), csv.GetString(pnColNo[SETTING2_FEATURETYPE]), _countof(m_tSETTING2Data.szFeatureType) - 1);	// KOMTAS Viewer機能選択
	strncpy_s(m_tSETTING2Data.szNcLoggerFileName, _countof(m_tSETTING2Data.szNcLoggerFileName), csv.GetString(pnColNo[SETTING2_NCLOGGER_FILENAME]), _countof(m_tSETTING2Data.szNcLoggerFileName) - 1);			// NCLogger生データファイル名

	// 特徴量データ
	for( INT32 i = 0 ; i < NOF_MES_FEATURE ; i++ ){
		nColOffset = i * ( SETTING2_FEATURE2_ENABLE - SETTING2_FEATURE1_ENABLE ) ;

		m_tSETTING2Data.taFeature[i].bEnable		= csv.GetBool  ( pnColNo[SETTING2_FEATURE1_ENABLE ] + nColOffset ) ;	// 有効フラグ
		
		// 判定は文字列から変換
		strTmp = csv.GetString( pnColNo[SETTING2_FEATURE1_JUDGE	 ] + nColOffset ) ;
		m_tSETTING2Data.taFeature[i].nJudge = ( strTmp == _T("OK") ) ? JUDGE_OK : JUDGE_NG ;

		m_tSETTING2Data.taFeature[i].nJudgeKind	= csv.GetInt32 ( pnColNo[SETTING2_FEATURE1_JDGKIND] + nColOffset ) ;	// 判定種
		m_tSETTING2Data.taFeature[i].dValue		= csv.GetDouble( pnColNo[SETTING2_FEATURE1_VALUE	 ] + nColOffset ) ;	// 計測値
		m_tSETTING2Data.taFeature[i].dMin		= csv.GetDouble( pnColNo[SETTING2_FEATURE1_MIN	 ] + nColOffset ) ;	// 計測値下限
		m_tSETTING2Data.taFeature[i].dMax		= csv.GetDouble( pnColNo[SETTING2_FEATURE1_MAX	 ] + nColOffset ) ;	// 計測値上限

		strncpy_s(m_tSETTING2Data.taFeature[i].szName, _countof(m_tSETTING2Data.taFeature[i].szName), csv.GetString(pnColNo[SETTING2_FEATURE1_NAME] + nColOffset), _countof(m_tSETTING2Data.taFeature[i].szName) - 1); // 特徴量名
	}

	return TRUE ;
}

/********************************************************************************
*	関数名：IsNewFolder
*	用　途：新規フォルダ作成判定式
*	引き数：
*	戻り値：新規作成が必要であればＴＲＵＥ、不要でＦＡＬＳＥ
*	備　考：
********************************************************************************/
BOOL CCollectSETTING2::IsNewFolder()
{
	return (m_nFileCnt >= m_nFileMax);
}

/********************************************************************************
*	関数名：MakeFolderName
*	用　途：フォルダ名の作成
*	引き数：フォルダ名格納場所
*	戻り値：作成成功でＴＲＵＥ、失敗でＦＡＬＳＥ
*	備　考：ログ監視クラスの監視対象になるフォルダ名である必要がある
********************************************************************************/
BOOL CCollectSETTING2::MakeFolderName(CString *str)
{
	SYSTEMTIME	tTime;

	GetLocalTime(&tTime);

	str->Format(
		_T("%04u%02u%02u%02u%02u"),
		tTime.wYear,
		tTime.wMonth,
		tTime.wDay,
		tTime.wHour,
		tTime.wMinute
	);

	return TRUE;
}