/********************************************************************************
*		ソースファイル名：CollectProductInfo.cpp										
*		用            途：生産情報収集クラス												
*		備            考：														
*		履            歴：														
********************************************************************************/
/****************************
*   コンパイル環境定数	    *
****************************/
// CollectSETTING4.cpp : 実装ファイル
//

#include "stdafx.h"
/****************************
*   インクルードファイル    *
****************************/
#include "CollectProductInfo.h"
#include "WMes.h"
#include "DailyLogManagerS.h"
#include "CsvFile.h"
/****************************
*        マクロ定義			*
****************************/
#define	THREAD_WAIT_MS		5000		// スレッド周期[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 ;	}

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

	m_nFileMax			= 1000;
	m_nFileCnt			= m_nFileMax + 1;

	m_pProductsInfoDataQueue= NULL;

	ClearData() ;
}

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

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

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

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

	if(m_pProductsInfoDataQueue == NULL){
		CDailyLogManagerS::GetInstance().PrintLog( LOG_ERROR, _T("[生産情報データ]データ格納キュー異常") ) ;
		return FALSE ;
	}

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

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

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

	m_bInitialize = TRUE ;

	CDailyLogManagerS::GetInstance().PrintLog(LOG_PRODUCTINFO, _T("[生産情報データ]データ収集開始：Path = %s"), m_strInputPath ) ;

	return m_bInitialize ;
}

/********************************************************************************
*	関数名：ClearData															
*	用　途：データクリア															
*	引き数：																	
*	戻り値：																	
*	備　考：																	
********************************************************************************/
void CCollectProductInfo::ClearData()
{
	// 工具ステータスデータクリア
	ZeroMemory( &m_tProductsInfoData, sizeof( PRODUCTSINFO_DATA ) ) ;
}

/*******************************************************************************
*	関　数　名：Wait
*	用　　　途：スレッド終了指示待ち
*	引　き　数：待ち時間[ms]
*	戻　り　値：スレッド終了指示あり(1) / なし(0) / エラー(-1)
*	備　　　考：通常エラーは通常ありえない
********************************************************************************/
INT32 CCollectProductInfo::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 CCollectProductInfo::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_PRODUCTINFO, m_bInputPath ) ;
		}

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

			// 工具ステータスデータファイル解析
			byNgFlag = 0x00 ;
			if( AnalyzeProductInfoFile( strFilePath ) )
			{
				CDailyLogManagerS::GetInstance().PrintLog(LOG_PRODUCTINFO, _T("[生産情報データ]データファイル検知： File = %s"), strFilePath ) ;

				// 工具ステータスデータキューへ格納
				m_pProductsInfoDataQueue->Push( (PVOID)&m_tProductsInfoData ) ;
			}

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

			m_nFileCnt++;
		}

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

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

	return TRUE ;
}

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

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

/********************************************************************************
*	関数名：AnalyzeProductInfoFile													
*	用　途：生産情報ファイル解析												
*	引き数：ファイルパス														
*	戻り値：															
*	備　考：									
********************************************************************************/
BOOL CCollectProductInfo::AnalyzeProductInfoFile( 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 ;

	// データは最終行取得
	for (int i = 1; i < CsvFile.GetCount(); i++)// ヘッダ行(Index=0は除く)
	{
		// ヘッダ行のみ or 行がない場合終了
		if (CsvFile.GetCount() <= 1) break;

		csv = CsvFile.GetAt(i);

		strTmp = csv.GetString(pnColNo[PRODUCTINFO_DATETIME]);
		strncpy_s(m_tProductsInfoData.taProductInfo[i - 1].szDateTime, _countof(m_tProductsInfoData.taProductInfo[i - 1].szDateTime), strTmp, _countof(m_tProductsInfoData.taProductInfo[i - 1].szDateTime) - 1);

		m_tProductsInfoData.taProductInfo[i - 1].nToolNo = csv.GetInt32(pnColNo[PRODUCTINFO_TOOLNO]);				// 工具番号
		m_tProductsInfoData.taProductInfo[i - 1].nToolCounts = csv.GetInt32(pnColNo[PRODUCTINFO_TOOLCOUNTS]);			// 工具カウント
		m_tProductsInfoData.taProductInfo[i - 1].nWorkCounts = csv.GetInt32(pnColNo[PRODUCTINFO_WORKCOUNTS]);			// ワークカウント
		m_tProductsInfoData.taProductInfo[i - 1].nWorkType = csv.GetInt32(pnColNo[PRODUCTINFO_WORKTYPE]);			// 車種
		strTmp = csv.GetString(pnColNo[PRODUCTINFO_WORKSERIAL]);
		strncpy_s(m_tProductsInfoData.taProductInfo[i - 1].szWorkSerial, _countof(m_tProductsInfoData.taProductInfo[i - 1].szWorkSerial), strTmp, _countof(m_tProductsInfoData.taProductInfo[i - 1].szWorkSerial) - 1);		// ワークシリアル
		m_tProductsInfoData.taProductInfo[i - 1].dCustomMacroVal1 = csv.GetDouble(pnColNo[PRODUCTINFO_CUSTOMMACRO1]);			// カスタムマクロ値1
		m_tProductsInfoData.taProductInfo[i - 1].dCustomMacroVal2 = csv.GetDouble(pnColNo[PRODUCTINFO_CUSTOMMACRO2]);			// カスタムマクロ値2
	}

	m_tProductsInfoData.nToolNum = CsvFile.GetCount() - 1;

	return TRUE ;
}

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

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