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

#include "stdafx.h"
/****************************
*   インクルードファイル    *
****************************/
#include "CollectMining.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 ;	}

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

	m_pMiningQueue		= NULL ;
	m_bInputPath		= FALSE ;

	m_nFileMax			= 1000;
	m_nFileCnt			= m_nFileMax + 1;

	ClearData() ;

}

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

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

// CCollectMining メッセージ ハンドラ
/********************************************************************************
*	関数名：Initialize															
*	用　途：初期化																
*	引き数：																	
*	戻り値：																	
*	備　考：																	
********************************************************************************/
BOOL CCollectMining::Initialize( CRingQueue* pMiningQueue )
{
	m_tpParam			= &CParameterManager::GetInstance().RefParamInit()->taCollect[COLLECT_MINING] ;
	m_strInputPath[0]	= m_tpParam->szDataPath ;		// OP10用
	m_strInputPath[1]	= m_tpParam->szDataPathSub1 ;	// OP20用
	m_strInputPath[2]	= m_tpParam->szDataPathSub2 ;	// OP30用
	m_strInputPath[3]	= m_tpParam->szDataPathSub3 ;	// OP40用
	m_strInputPath[4]	= m_tpParam->szDataPathSub4 ;	// OP50用
	m_strInputPath[5]	= m_tpParam->szDataPathSub5 ;	// OP60用
	m_pMiningQueue		= pMiningQueue ;

	m_nFileMax			= m_tpParam->uFilesInFolder;
	m_nFileCnt			= m_nFileMax + 1;

	m_bInputPath = TRUE ;
	for( INT32 i = 0 ; i < NOF_OP ; i++ ){
		if( !CheckInputPath( m_strInputPath[i] ) ){
			CDailyLogManagerS::GetInstance().PrintLog( LOG_ERROR, _T("[マイニングデータ]データ入力パス異常：Path = %s"), m_strInputPath[i] ) ;
			m_bInputPath = FALSE ;
		}
	}

	if( m_pMiningQueue == NULL ){
		CDailyLogManagerS::GetInstance().PrintLog( LOG_ERROR, _T("[マイニングデータ]データ格納キュー異常") ) ;
		return FALSE ;
	}

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

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

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

	m_bInitialize = TRUE ;

	for( INT32 i = 0 ; i < NOF_OP ; i++ ){
		CDailyLogManagerS::GetInstance().PrintLog( LOG_MINING, _T("[マイニングデータ]データ%d収集開始：Path = %s"), i + 1, m_strInputPath[i] ) ;
	}

	return m_bInitialize ;
}

/********************************************************************************
*	関数名：ClearData															
*	用　途：データクリア															
*	引き数：																	
*	戻り値：																	
*	備　考：																	
********************************************************************************/
void CCollectMining::ClearData()
{
	// マイニングデータクリア
	ZeroMemory( &m_tMiningData , sizeof( MINING_DATA ) ) ;
}

/*******************************************************************************
*	関　数　名：Wait
*	用　　　途：スレッド終了指示待ち
*	引　き　数：待ち時間[ms]
*	戻　り　値：スレッド終了指示あり(1) / なし(0) / エラー(-1)
*	備　　　考：通常エラーは通常ありえない
********************************************************************************/
INT32 CCollectMining::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 CCollectMining::Main()
{
	CString	strFilePath, strMovePath, strFileName, strTmp, strNewPath;
	BYTE	byNgFlag ;
	BOOL	bInputPath ;

	while( Wait(0) == 0 ){
		// 入力元パス確認
		bInputPath = TRUE ;
		for( INT32 i = 0 ; i < NOF_OP ; i++ ){
			bInputPath &= CheckInputPath( m_strInputPath[i] ) ;
		}

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

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

		// 入力元にファイルあれば解析開始
		for( INT32 i = 0 ; i < NOF_OP ; i++ ){
			if( FindMiningFile( m_strInputPath[i], &strFilePath ) ){

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

				// マイニングデータファイル解析
				byNgFlag = 0x00 ;
				if( AnalyzeMiningFile( strFilePath ) )
				{
					CDailyLogManagerS::GetInstance().PrintLog( LOG_MINING, _T("[マイニングデータ]データファイル検知： File = %s"), strFilePath ) ;

					// マイニングデータキューへ格納
					m_pMiningQueue->Push( (PVOID)&m_tMiningData ) ;

					// 折損前検知の場合はインプロセス計測データキューにも格納
					if( m_tMiningData.bBreakage ){
						CDailyLogManagerS::GetInstance().PrintLog( LOG_MINING, _T("[マイニングデータ]折損前検知有り") ) ;
					}
				}

				// 解析完了したファイルはリネーム 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_MINING, _T("[マイニングデータ]データファイル解析完了： MoveFile = %s"), strMovePath ) ;

				m_nFileCnt++;
			}

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

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

	return TRUE ;
}

/*******************************************************************************
*	関　数　名：UpFolderCopy
*	用　　　途：上位フォルダコピー
*	引　き　数：
*	戻　り　値：
*	備　　　考：
********************************************************************************/
void CCollectMining::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 ) ;
}

/********************************************************************************
*	関数名：FindMiningFile													
*	用　途：マイニングデータファイル検索												
*	引き数：ファイルパス格納先														
*	戻り値：															
*	備　考：1つでも該当ファイルあれば終了									
********************************************************************************/
BOOL CCollectMining::FindMiningFile( 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 = FindMiningFile( 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 ;
}

/********************************************************************************
*	関数名：AnalyzeMiningFile													
*	用　途：マイニングデータファイル解析												
*	引き数：ファイルパス														
*	戻り値：															
*	備　考：									
********************************************************************************/
BOOL CCollectMining::AnalyzeMiningFile( 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_tMiningData.szWorkID, _countof(m_tMiningData.szWorkID), csv.GetString(pnColNo[MINING_WORKID]), _countof(m_tMiningData.szWorkID) - 1);	// ワークID
	strncpy_s(m_tMiningData.szToolID, _countof(m_tMiningData.szToolID), csv.GetString(pnColNo[MINING_TOOLID]), _countof(m_tMiningData.szToolID) - 1);	// ツールID

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

	m_tMiningData.nOpNo		 = csv.GetInt32 ( pnColNo[MINING_OPNO		] ) ;	// OP番号
	m_tMiningData.nONo		 = csv.GetInt32 ( pnColNo[MINING_ONO		] ) ;	// O番号
	m_tMiningData.nToolNo	 = csv.GetInt32 ( pnColNo[MINING_TOOLNO		] ) ;	// ツール番号
	m_tMiningData.nToolCount = csv.GetInt32 ( pnColNo[MINING_TOOLCOUNT	] ) ;	// ツールカウント
	m_tMiningData.dValue	 = csv.GetDouble( pnColNo[MINING_VALUE		] ) ;	// 計測値
	m_tMiningData.dMax		 = csv.GetDouble( pnColNo[MINING_MAX		] ) ;	// 計測値上限
	m_tMiningData.nLineID	 = csv.GetInt32 ( pnColNo[MINING_LINEID		] ) ;	// ラインID

	// 判定は文字列から変換
	strTmp = csv.GetString( pnColNo[MINING_JUDGE] ) ;
	m_tMiningData.nJudge = ( strTmp == _T("OK") ) ? JUDGE_OK : JUDGE_NG ;

	// 折損前検知フラグは文字列から変換
	strTmp = csv.GetString( pnColNo[MINING_BREAKAGE] ) ;
	m_tMiningData.bBreakage = ( strTmp == _T("OK") ) ? FALSE : TRUE ;

	// 項目診断
	for( INT32 i = 0 ; i < NOF_JUDGE_ITEM_RANK ; i++ ){
		nColOffset = i * ( MINING_IMP2_SENSOR - MINING_IMP1_SENSOR ) ;

		m_tMiningData.taJudgeItem[i].nItem		= csv.GetInt32 ( pnColNo[MINING_IMP1_SENSOR	] + nColOffset ) ;
		m_tMiningData.taJudgeItem[i].nFeature	= csv.GetInt32 ( pnColNo[MINING_IMP1_FEATURE] + nColOffset ) ;
		m_tMiningData.taJudgeItem[i].dValue		= csv.GetDouble( pnColNo[MINING_IMP1_VALUE	] + nColOffset ) ;
	}

	strncpy_s(m_tMiningData.szIpAddress, _countof(m_tMiningData.szIpAddress), csv.GetString(pnColNo[MINING_IPADDRESS]), _countof(m_tMiningData.szIpAddress) - 1);		// IpAddress(加工機)
	strncpy_s(m_tMiningData.szModelName, _countof(m_tMiningData.szModelName), csv.GetString(pnColNo[MINING_MODELNAME]), _countof(m_tMiningData.szModelName) - 1);		// ModelName(加工機名)
	strncpy_s(m_tMiningData.szFeatureType, _countof(m_tMiningData.szFeatureType), csv.GetString(pnColNo[MINING_FEATURETYPE]), _countof(m_tMiningData.szFeatureType) - 1);	// KOMTAS Viewer機能選択
	strncpy_s(m_tMiningData.szNcLoggerFileName, _countof(m_tMiningData.szNcLoggerFileName), csv.GetString(pnColNo[MINING_NCLOGGER_FILENAME]), _countof(m_tMiningData.szNcLoggerFileName) - 1);	// NCLogger生データファイル名

	// 折損前検知有りならダミーインプロセス計測データ作成
	//if( m_tMiningData.bBreakage ){

	//	// 計測日時はマイニングデータから取得
	//	strncpy_s( m_taInprocessData[m_nInpBufIndex].szDateTime, m_tMiningData.szDateTime, DATETIME_BUFLEN ) ;

	//	// OP10の時だけはワーク情報無いので仮データ入れるしかない
	//	if( m_tMiningData.nOpNo == 10 ){
	//		m_taInprocessData[m_nInpBufIndex].nKindNo	= 10 ;				// 品種番号
	//		m_taInprocessData[m_nInpBufIndex].nInsKind	= INSKIND_NORMAL ;	// 検査種
	//	}

	//	// ダミーの計測データを格納する
	//	P_INP_MEASURE_DATA	tpData = &m_taInprocessData[m_nInpBufIndex].apMeasureData[0] ;
	//	tpData->nGroup			= 1 ;
	//	tpData->nNo				= 1 ;
	//	tpData->nOpNo			= m_tMiningData.nOpNo ;
	//	tpData->nHoleNo			= 999 ;
	//	tpData->dCoordX			= 0.0 ;					// X座標
	//	tpData->dCoordY			= 0.0 ;					// Y座標
	//	tpData->nInpJudge		= INP_JUDGE_OK ;		// 計測値判定
	//	tpData->dInpMin1		= 0.0 ;					// 計測値下限1
	//	tpData->dInpMax1		= 0.0 ;					// 計測値上限1
	//	tpData->dInpMin2		= 0.0 ;					// 計測値下限2
	//	tpData->dInpMax2		= 0.0 ;					// 計測値上限2
	//	tpData->dInpValue		= 0.0 ;					// 計測値
	//	tpData->dInpNgValue		= 0.0 ;					// 計測値NG量

	//	for( INT32 i = 0 ; i < NOF_INP_TOOL ; i++ ){
	//		tpData->taToolData[i].nToolNo		= ( i == 0 ) ? m_tMiningData.nToolNo : -1 ;	// ツールNo
	//		tpData->taToolData[i].bFirstCheck	= FALSE ;									// ツール初品チェック
	//		tpData->taToolData[i].nUseOpNo		= ( i == 0 ) ? m_tMiningData.nOpNo	 : 0 ;	// 使用OP No
	//	}

	//	strTmp.Format( _T("データマイニング") ) ; strncpy_s( tpData->szInsItem	 , strTmp, INSPECT_BUFLEN ) ;		// 検査項目
	//	strTmp.Format( _T("折損前検知") )		; strncpy_s( tpData->szInsContent, strTmp, INSPECT_BUFLEN ) ;		// 検査内容

	//	// データ数は1件だけ
	//	m_taInprocessData[m_nInpBufIndex].nNofData = 1 ;
	//	
	//}

	return TRUE ;
}

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

/********************************************************************************
*	関数名：MakeFolderName
*	用　途：フォルダ名の作成
*	引き数：フォルダ名格納場所
*	戻り値：作成成功でＴＲＵＥ、失敗でＦＡＬＳＥ
*	備　考：ログ監視クラスの監視対象になるフォルダ名である必要がある
********************************************************************************/
BOOL CCollectMining::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;
}