
//
// Trust Design LLC : SECS/HSMS Communication library
//
// (c) Copyright Trust Design LLC.  2010-2019.  All rights reserved.
//

// =============================================================================
//
// TDSSample2Proc : テスト 及び サンプル・プログラム
//
//  指定の SF コードのメッセージを送信する。
//  Callback 関数を使用して、受信メッセージに対する応答処理を行う。その際、
//  メッセージ定義ファイルを使用した自動応答メッセージ構築機能を使用する。
//  SML 形式メッセージ定義ファイルを使用して SECS メッセージの構築、解析を
//  行う。
//
//  (注1) もちろん、TDS はメッセージ定義ファイルを用いずに SECS メッセージ
//  の構築、解析を行うことができる。メッセージ定義ファイルを用いない場合の
//  SECS メッセージの構築、解析に関しては Sample0/ の各サンプル・ソースを
//  参照すること。
//
//  Build を行うと、実行形式ファイル（TDSSample2.exe）が Release/ もしくは
//  Debug/ に作成される。
//  実行には、以下のファイルが必要。../ 及び ../Sample0/ に存在する以下の
//  ファイルを、本ディレクトリにコピーすること。
//  ・ Sample.ini
//  ・ Sample.sml
//  ・ TDS.dll
//
//  下記の #define SEND_WAIT 値を変更すると、連続送信における送信間隔を変更
//  することができる。
//
//
// 起動方法
//
//   TDSSample2
//   ~~~~~~~~~~
// =============================================================================

#include			"stdafx.h"
#include			"TDSSample2Proc.h"


// -----------------------------------------------------------------------------

extern void		 	XXPrintf		(char*,...);

#define 			PO				XXPrintf
#define				RECV_WAIT		100			// メッセージ受信監視間隔  (m秒)
#define				SEND_WAIT		2000		// メッセージ送信間隔      (m秒)
#define				SEND_NO			10			// メッセージ送信休みまでの回数
#define				SEND_SLEEP		10000		// お休み時間              (m秒)
#define				ERROR_SLEEP		10000		// エラー発生時お休み時間  (m秒)

#define				PARAMFILE		"Sample.ini"




// =============================================================================
// 構築 及び 破壊 --------------------------------------------------------------

CTDSSample2Proc::CTDSSample2Proc()
{
  m_Fd=m_Md=m_Status=m_OType=m_Stop0=m_Stop1=m_CRun=m_CRunP=0;
  m_Thread1=m_Thread0=0;
  InitializeCriticalSection(&m_cs0);
  InitializeCriticalSection(&m_cs1);
}



CTDSSample2Proc::~CTDSSample2Proc()
{
  ContSendStop();
  if(m_Md>0)	_TDSMDMssgTerminate	(m_Md,0);
  if(m_Fd>0)	_TDSUDrvClose		(m_Fd,0);
  DeleteCriticalSection(&m_cs0);
  DeleteCriticalSection(&m_cs1);
}



// =============================================================================
// 初期化 及び 終了処理 --------------------------------------------------------

int
CTDSSample2Proc::Initialize(		//  o : < 0 : NG
									//		= 0 : OK
int					type)			// i  : 処理タイプ
									//		= 0 : 装置
									//		  1 : ホスト
{
  static char		*ctype[]={"EQUIP","HOST"};
  int				rtn;	

  if((m_Fd=_TDSUDrvOpen(0x02,PARAMFILE,ctype[m_OType=type],0))	> 0){
	m_Md=_TDSMDMssgInitialize(0x4000,m_Fd,0);
  }
  PO("Opened (%d)"	,m_Fd);

  ContRecvStart();

  if((rtn=m_Fd)> 0)	rtn=0;
  return(rtn);
}


int
CTDSSample2Proc::Terminate()
{
  int				fd;

  fd=m_Fd;

  ContSendStop();
  ContRecvStop();

	// 終了指示を受け付け、終了処理を行うスレッドとは別のスレッドで動作する
	// 受信動作が完了するまで待つために、ロック制御を行う。
	// ロックできない場合は、受信動作実行中で、画面へのメッセージ表示を行って
	// いる最中なので、一旦本関数から抜け、再度の本関数の Call を期待する。
  if(TryEnterCriticalSection(&m_cs0)	==0)	return(-1);

  if(m_Md>0)	_TDSMDMssgTerminate	(m_Md,0);	m_Md=0;
  if(m_Fd>0)	_TDSUDrvClose		(m_Fd,0);	m_Fd=0;
  LeaveCriticalSection(&m_cs0);

  PO("Closed (%d)"	,fd);

  return(0);
}


// =============================================================================
// 現在動作状態の取得  ---------------------------------------------------------

int
CTDSSample2Proc::GetStatus()		//  o : = 0 : 未接続
									//		  1 : 未 Select
									//		  2 : Select 状態
{
  return(m_Status);
}



int
CTDSSample2Proc::GetCRun()			//  o : = 0 : 未実行
									//		  1 : 実行中
{
  return(m_CRun);
}



// =============================================================================
// S1F1 メッセージ構築及び送信 -------------------------------------------------

int
CTDSSample2Proc::SendS1F1()
{
  TDSECSHead		hd;
  char				msg[16];
  int				rtn,len=0;

					// Sample0/SubFunction.h の SendS1F1() 内 (注) を参照。
  rtn=_TDSUDrvSend(m_Fd,0x0000,0,0x8101,0,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// -----------------------------------------------------------------------------
// S1F2 メッセージ (Host) 構築及び送信 -----------------------------------------

int
CTDSSample2Proc::SendS1F2H(
int					did,		// i  : 受信１次メッセージの DeviceID
unsigned int		xid)		// i  : 受信１次メッセージの TX-ID
{
  TDSECSHead		hd;
  char				msg[256];
  int				rtn,len;
  
  _TDSMDMssgInit	(m_Md,0,msg,sizeof(msg),"S1F2_H");
  len=_TDSMDMssgEnd	(m_Md,0,msg);

  if(len<0)	rtn=len;
  else		rtn=_TDSUDrvSend (m_Fd,0x0000,did,0x0102,xid,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// -----------------------------------------------------------------------------
// S1F2 メッセージ (Equip) 構築及び送信 ----------------------------------------

int
CTDSSample2Proc::SendS1F2E(
int					did,		// i  : 受信１次メッセージの DeviceID
unsigned int		xid)		// i  : 受信１次メッセージの TX-ID
{
  TDSECSHead		hd;
  char				msg[256];
  int				rtn,len;
  
  _TDSMDMssgInit	(m_Md,0,msg,sizeof(msg),"S1F2_E");
  len=_TDSMDMssgEnd	(m_Md,0,msg);

  if(len<0)	rtn=len;
  else		rtn=_TDSUDrvSend (m_Fd,0x0000,did,0x0102,xid,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// -----------------------------------------------------------------------------
// S2F49 メッセージ構築及び送信 ------------------------------------------------

int
CTDSSample2Proc::SendS2F49()
{
  static int		cnt=0;
  TDSECSHead		hd;
  char				msg[1024],itm[32],str[128];
  int				rtn,no1,no2,len,i,j;

  sprintf_s(str,sizeof(str),"LOTID (%4d)",++cnt);
  no1=(cnt%2)+1;	no2=(cnt%10)+1;

  _TDSMDMssgInit	(m_Md,0,msg,sizeof(msg),"S2F49");
  _TDSMDMssgBuild	(m_Md,0,msg,"LOTID"	, 1,str);			// LOTID
  _TDSMDMssgBuild	(m_Md,0,msg,"NOI1"	, 1,&no1);			// L[no1]
  for(i=0;i<no1;i++){
	sprintf_s(itm,sizeof(itm),"NOI2:%d"				    	,i+1);
	_TDSMDMssgBuild	(m_Md,0,msg,itm		, 1,&no2);			//  L[no2]
	for(j=0;j<no2;j++){
	  sprintf_s(itm,sizeof(itm),"WAFERID:%d:%d"		    	,i+1,j+1);
	  sprintf_s(str,sizeof(str),"WAFER[%04d-%d-%02d]"	,cnt,i+1,j+1);
 	  _TDSMDMssgBuild(m_Md,0,msg,itm	, 0,str);			//   WAFERID
	  sprintf_s(itm,sizeof(itm),"PPID:%d:%d"		    	,i+1,j+1);
	  sprintf_s(str,sizeof(str),"PPID [%04d-%d-%02d]"	,cnt,i+1,j+1);
 	  _TDSMDMssgBuild(m_Md,0,msg,itm	, 0,str);			//   PPID
  } }
  len=_TDSMDMssgEnd	(m_Md,0,msg);

  if(len<0)	rtn=len;	// Sample0/SubFunction.h の SendS1F1() 内 (注) を参照。
  else		rtn=_TDSUDrvSend (m_Fd,0x0000,0,0x8200+49,0,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// -----------------------------------------------------------------------------
// S2F50 メッセージ構築及び送信 ------------------------------------------------

int
CTDSSample2Proc::SendS2F50(
int					did,		// i  : 受信１次メッセージの DeviceID
unsigned int		xid)		// i  : 受信１次メッセージの TX-ID
{
  static int		cnt=0;
  TDSECSHead		hd;
  char				msg[256],str[128];
  int				rtn,len;
  
  sprintf_s(str,sizeof(str),"LOTID (%4d)",++cnt);

  _TDSMDMssgInit	(m_Md,0,msg,sizeof(msg),"S2F50");
  _TDSMDMssgBuild   (m_Md,0,msg,"LOTID",  0,str);			//   LOTID
  len=_TDSMDMssgEnd	(m_Md,0,msg);

  if(len<0)	rtn=len;
  else		rtn=_TDSUDrvSend (m_Fd,0x0000,did,0x0200+50,xid,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// -----------------------------------------------------------------------------
// S6F11 メッセージ構築及び送信 ------------------------------------------------

int
CTDSSample2Proc::SendS6F11()
{
  static int		cnt=0;
  TDSECSHead		hd;
  char				msg[256];
  unsigned short	vs[1];
  int				rtn,len;

  if((++cnt)==65536) cnt=0;		vs[0]=cnt;
  _TDSMDMssgInit	(m_Md,0,msg,sizeof(msg),"S6F11_0");
  _TDSMDMssgBuild	(m_Md,0,msg,"DATAID",1,vs);				//  DATAID
  len=_TDSMDMssgEnd	(m_Md,0,msg);

  if(len<0)	rtn=len;	// Sample0/SubFunction.h の SendS1F1() 内 (注) を参照。
  else		rtn=_TDSUDrvSend (m_Fd,0x0000,0,0x8600+11,0,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// -----------------------------------------------------------------------------
// S6F12 メッセージ構築及び送信 ------------------------------------------------

int
CTDSSample2Proc::SendS6F12(
int					did,		// i  : 受信１次メッセージの DeviceID
unsigned int		xid)		// i  : 受信１次メッセージの TX-ID
{
  TDSECSHead		hd;
  char				msg[256];
  int				rtn,len;

  _TDSMDMssgInit	(m_Md,0,msg,sizeof(msg),"S6F12");
  len=_TDSMDMssgEnd	(m_Md,0,msg);

  if(len<0)	rtn=len;
  else		rtn=_TDSUDrvSend (m_Fd,0x0000,did,0x0600+12,xid,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// -----------------------------------------------------------------------------
// 指定メッセージ名称のメッセージ構築及び送信 ----------------------------------

int
CTDSSample2Proc::SendNamedMssg(
char				*name)
{
  TDSECSHead		hd;
  char				msg[4096];		// 最大 4096Bytes とする
  int				rtn,len,sf,scd,fcd;

	// メッセージ定義ファイルの変更をこの時点で反映するため、ここで再度初期化
	// 処理を行う。もとより、メッセージ定義ファイルの変更がないのであれば、こ
	// の処理は必要ない。また、ここで再初期化するのは、その後の _TDSMssgInit()
	// 等の処理に関するものであり、トレース表示は既初期化済みの情報により行わ
	// れるので、必ずしも最新メッセージ情報が使用される訳ではないことに留意す
	// ること。
  if(m_Md>0)	_TDSMDMssgTerminate	(m_Md,0);
  m_Md=			_TDSMDMssgInitialize(0x4000,m_Fd,0);

  sscanf_s(name,"S%dF%d"	,&scd,&fcd);
  sf = (scd<<8) | fcd;
  if(scd!=9 && (fcd%2)!=0)	sf |= 0x8000;

  _TDSMDMssgInit	(m_Md,0,msg,sizeof(msg),name);
  len=_TDSMDMssgEnd	(m_Md,0,msg);

  if(len<0)	rtn=len;	// Sample0/SubFunction.h の SendS1F1() 内 (注) を参照。
  else		rtn=_TDSUDrvSend (m_Fd,0x0000,0,sf,0,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// =============================================================================
// 受信処理 --------------------------------------------------------------------

static UINT
ContRecv(
void				*par)		// i  : 本 CLASS インスタンスへのポインタ
{
  CTDSSample2Proc	*pt;
  TDSECSHead		hd;
  char				msg[1024],rname[32],sname[32];
  unsigned int		xid;
  int				rtn,len,dev,sf,sta;

  PO("受信処理スレッド起動");
  pt=(CTDSSample2Proc*)par;

  for(pt->m_Stop0=0; pt->m_Stop0==0;){
	if	  ((sta=_TDSUDrvStatus(pt->m_Fd,0))	==3)	pt->m_Status=2;
	else if(sta								==0)	pt->m_Status=0;
	else											pt->m_Status=1;

	rtn=_TDSUDrvRecv (pt->m_Fd,0x0000,&dev,&sf,&xid,msg,sizeof(msg),&hd);
	if(rtn==(-E_NODATA)){	Sleep(RECV_WAIT);					continue;}

	if(pt->m_Stop0		!=0)									break;
	EnterCriticalSection(&pt->m_cs0);
	pt->DispData(1,&hd,msg,rtn,rtn);
	if(rtn>=0 && pt->m_Md>0){
	  if((len=_TDSMDMssgAutoRes(pt->m_Md,0,&hd
				,msg,rtn,sizeof(msg),rname,sname,&sf))	>=0){
		PO("RECV %s ..  Automatically responds %s [S%dF%d]",rname,sname,sf/0x100,sf&0xff);
		rtn=_TDSUDrvSend(pt->m_Fd,0x0000,0,sf,xid,msg,len,&hd);
		pt->DispData(2,&hd,msg,len,rtn);
	  }else{
		if(len!=(-930) && len!=(-931)) PO("RECV Auto response error (%d)",len);
	} }
	LeaveCriticalSection(&pt->m_cs0);
	if(rtn	< 0)			Sleep(RECV_WAIT);	
  }

  pt->m_Thread0=0;	pt->m_Stop0=2;
  PO("受信処理スレッド停止");
  AfxEndThread(0);
  return(0);
}



void
CTDSSample2Proc::ContRecvStart()
{
  if(m_Thread0==0)	m_Thread0=AfxBeginThread(ContRecv,(LPVOID)this);
}



void
CTDSSample2Proc::ContRecvStop()
{
  if(m_Thread0!=0)	m_Stop0=1;
}



// =============================================================================
// 連続送信処理 ----------------------------------------------------------------

static UINT
ContSend(
void				*par)		// i  : 本 CLASS インスタンスへのポインタ
{
  CTDSSample2Proc	*pt;
  int				rtn,no;

  PO("連続送信処理スレッド起動");
  pt=(CTDSSample2Proc*)par;	pt->m_CRun=1;	pt->m_CRunP=0;

  for(pt->m_Stop1=no=0; pt->m_Stop1==0;){
	Sleep(SEND_WAIT);
	if(pt->m_Stop1		!=0)									break;
	if(pt->m_Status		!=2 &&	pt->m_CRunP!=0)					break;
	if(pt->m_Status		==2){	pt->m_CRunP=1;
	  if(pt->m_OType	==0)	rtn=pt->SendS6F11();
	  else						rtn=pt->SendS2F49();
	  if(rtn			< 0)	Sleep(ERROR_SLEEP);
	  else{						no++;
		if((no%SEND_NO)	==0)	Sleep(SEND_SLEEP);
  } } }

  pt->m_Thread1=0;	pt->m_Stop1=2;	pt->m_CRun=0;
  PO("連続送信処理スレッド停止");
  AfxEndThread(0);
  return(0);
}



void
CTDSSample2Proc::ContSendStart()
{
  if(m_Thread1==0)	m_Thread1=AfxBeginThread(ContSend,(LPVOID)this);
}



void
CTDSSample2Proc::ContSendStop()
{

  if(m_Thread1!=0)	m_Stop1=1;
}



// =============================================================================
// 送受信データを表示する。-----------------------------------------------------

int
CTDSSample2Proc::DispData(
int					tp,			// i  : メッセージ種別
								//		=0 : 送信結果
								//		 1 : 受信メッセージ
								//		 2 : 送信メッセージ
TDSECSHead			*hd,		// i  : SECS メッセージ・ヘッダ
void				*msg,		// i  : SECS メッセージ本体
int					len,		// i  : msg の長さ
int					rtn)		// i  : I/O の戻り値
{
  char				str[128];
  int				err=0,pos=0,ok=0;

  EnterCriticalSection(&m_cs1);
  if(rtn<0){				ok=1;
    if((rtn<(-E_NOTCONNECT) || (-E_ILLBLOCK)<rtn) && rtn!=(-E_NODATA)){
      if(m_Fd>0) _TDSErrorStatus(m_Fd,&err,&pos);
      sprintf_s(str,sizeof(str),"ERROR  [%d.%d.%d]",rtn,err,pos);
      if      (rtn==(-ENODEV)){
		PO("%s : No such device ID\n%49s"	,str,"");	len=ok=0;
      }else if(rtn==(-E2BIG )){
		PO("%s : Data size to large\n%49s"	,str,"");	len=ok=0;
      }else			PO("%s"					,str);
   	}else{
      sprintf_s(str,sizeof(str),"STATUS = %d : ",-rtn);
      switch(-rtn){
		case E_NODATA:		PO("%sNo data"			,str);		break;
		case E_ILLBLOCK:	PO("%sIllegal block#"	,str);		break;
		case E_T1TIMEDOUT:	PO("%sT1 Timeout occur"	,str);		break;
		case E_T2TIMEDOUT:	PO("%sT2 Timeout occur"	,str);		break;
		case E_T3TIMEDOUT:	PO("%sT3 Timeout occur"	,str);		break;
		case E_T4TIMEDOUT:	PO("%sT4 Timeout occur"	,str);		break;
		case E_T5TIMEDOUT:	PO("%sT5 Timeout occur"	,str);		break;
		case E_T6TIMEDOUT:	PO("%sT6 Timeout occur"	,str);		break;
		case E_T7TIMEDOUT:	PO("%sT7 Timeout occur"	,str);		break;
		case E_T8TIMEDOUT:	PO("%sT8 Timeout occur"	,str);		break;
		case E_RETRYOVER:	PO("%sRetry over"		,str);		break;
		case E_CONNECT:		PO("%sConnected"		,str);		break;
		case E_SELECT:		PO("%sSelected   (0x%04x)"
									,str,hd->did);				break;
		case E_REJECT:		PO("%sRejected XId=0x%04x.0x%04x"
									,str,hd->sid,hd->xid);		break;
		case E_DESELECT:	PO("%sDeselected (0x%04x)"
									,str,hd->did);				break;
		case E_NOTCONNECT:	PO("%sNot connected"	,str);		break;
		default:			PO("%s"					,str);		break;
  } } }

  if(ok==0) DispSECSMssg(tp,hd,msg,len);
  LeaveCriticalSection(&m_cs1);

  return(0);
}



// -----------------------------------------------------------------------------
// SECS メッセージを表示する。 -------------------------------------------------

void
CTDSSample2Proc::DispSECSMssg(
int					tp,			// i  : メッセージ種別
								//		=0 : 送信結果
								//		 1 : 受信メッセージ
								//		 2 : 送信メッセージ
TDSECSHead			*hd,		// i  : SECS メッセージ・ヘッダ
void				*msg,		// i  : SECS メッセージ格納領域（ヘッダ含まず）
int					len)		// i  : SECS メッセージのバイト長
{
  static char		*ctp[]={"SRES","RECV","SEND"};
  char				str[512],sfcode[12],rbit,wbit;
  int				rtn,md=0,dp,fm,form,noi,la,i;

  rbit=' ';			if((hd->did&0x8000)!=0)	rbit='R';
  wbit=' ';			if((hd->scd&  0x80)!=0)	wbit='W';
  sprintf_s(sfcode,sizeof(sfcode),"S%dF%d",hd->scd&0x7f,hd->fcd);
  PO("[%s]  Dev=0x%04x  %-8s  %c%c  XId=0x%04x.0x%04x  Len=%4d"
			,ctp[tp],hd->did&0x7fff,sfcode,rbit,wbit,hd->sid,hd->xid,len);

  if(msg!=0){
    if(tp==1){		dp=0x30a0;	fm=0xb000;}		// 受信の場合
    else{			dp=0x20a0;	fm=0xa000;}		// 送信の場合
	if(m_Fd>0 && (md=_TDSMssgFind(fm,msg,len,m_Fd,hd,str))> 0){
      if(str[0]!='\0')	PO("[%s]",str);			// メッセージ名称
      for(la=0;;){
		rtn=_TDSMssgNextL(md,dp,msg,&form,&noi,str,sizeof(str));
		if(rtn													< 0)	break;
	  	for(i=la;i>rtn;i--)		PO("%*s>",i*2	,"");	// '>' 出力
		la=rtn;											// 現在の階層を保存
		PO("  %s",str);									// 項目を表示
		if(form==000 && noi==0)	PO("%*s>",la*2+2,"");	// L0 の場合の '>' 処理
	  }
	  for(i=la;i>0;i--)			PO("%*s>",i*2	,"");	// 残りの '>' 表示
      _TDSMssgExit(md,0,msg);
  } }
}
