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

#include	"TDS.h"
#include	<string.h>
#include	<signal.h>


#if !defined	PO
#define		PO		fprintf(stdout
#endif
#if !defined	PE
#define		PE		fprintf(stderr
#endif

#if defined	WINDOWS
#include	<process.h>
#else
#include	<pthread.h>
#define		CRITICAL_SECTION	pthread_mutex_t
#endif

				// 以下のマクロ定数に関しては、BasicIo.c、
				// CallbackIo.c の先頭部分のコメント参照する事。

#if !defined	MSSG_USE_FILE
#define		MSSG_USE_FILE	0x00	// メッセージ定義ファイル使用の有無
					// 0x00 : 使用しない
					// 0x80 : 使用して項目名表示する
#endif

#if !defined	MSSG_DISP_TYPE
#define		MSSG_DISP_TYPE	0x00	// SECS メッセージ表示形式のデフォルト
					// 0x00 : 通常形式
					// 0x10 : ????形式
					// 0x20 : SML 形式
					// 0x40 : NSG/TS300 形式
#endif

#if !defined	MSSG_USE_NEXTL
#define		MSSG_USE_NEXTL	0	// MssgNextL を使用するか否か
					// 0    : 使用しない
					// 1    : 使用する
#endif


static int	Fd=0;		// 通信用識別子
static int	Md=0;		// メッセージ解析用識別子
static int	OType=0;	// 動作タイプ (0:Host 1:Equip)
static int	Break=0;	// シグナル受信状態

static CRITICAL_SECTION	Cs0;	// メッセージ構築処理ロック用	
				// 連続送受信処理をメッセージ定義ファイ ルを使用
				// して行う場合、上記 Md で識別するメッセージ構
				// 築処理において、１次メッセージの構築と、
				// Receive に対する２次メッセージの構築とがバッ
				// ティングする可能性があるので、それを避けるた
				// めにロック制御する。
				// もちろん、送信用と受信用とで別の Md を用意す
				// る場合には、ロック制御の必要はない。
				// また、V12.030 以降、メッセージ定義の初期化
				//  Call（_TDSMDMssgInitialize()）において、
				// (mode&0x4000)!=0 を指定するとロック制御をライ
				// ブラリ内部で行うので、ＡＰがロック制御を行う
				// 必要はない。本サンプルでは、このロック制御を
				// ライブラリ側で行うよう設定した上で、従来の
				// ロック制御の部分をコメント化して残す。

static CRITICAL_SECTION	Cs1;	// メッセージ表示処理ロック用	
				// 以下の DispData() で送受信メッセージ構造を表
				// 示する場合、CallbackIo.exe では、送信と受信を
				// 異なるスレッドで実行するため、同時にメッセー
				// ジ表示を行う場合が発生し、表示が乱れる可能性
				// がある。それを避けるため、ロック制御を行う。


/* ========================================================================== */
/* 共通関数 ================================================================= */

#if !defined	WINDOWS
static void		
InitializeCriticalSection(
CRITICAL_SECTION	*cs)
{
  pthread_mutex_init(cs,0);
}

static void
DeleteCriticalSection(
CRITICAL_SECTION	*cs)
{
  pthread_mutex_destroy(cs);
}

static void
EnterCriticalSection(
CRITICAL_SECTION	*cs)
{
  pthread_mutex_lock(cs);
}

static void
LeaveCriticalSection(
CRITICAL_SECTION	*cs)
{
  pthread_mutex_unlock(cs);
}


static void*
_beginthread(
void*			(*func)(void*),
int			stacksz,
void			*par)
{
  pthread_t		th;
  pthread_attr_t	ta;

  if(pthread_attr_init(&ta)				==0){
    pthread_attr_setdetachstate(&ta,PTHREAD_CREATE_DETACHED);
    if(stacksz>0) pthread_attr_setstacksize(&ta,stacksz);
    pthread_attr_destroy(&ta);
  }
  return((void*)pthread_create(&th,&ta,func,par));
}


static void
Sleep(
int			msec)
{
  struct timespec	itimer,otimer;

  itimer.tv_sec =(msec/1000);
  itimer.tv_nsec=(msec%1000)*1000*1000;
  nanosleep(&itimer,&otimer);
}
#endif



/* -------------------------------------------------------------------------- */


static void
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"};
  TDSAllType	*itm;
  char		item[64],str[512],sfcode[12],rbit,wbit;
  int		rtn,md=0,dp=MSSG_USE_FILE|MSSG_DISP_TYPE,fm,form,sz,noi,la,i;
	// [注意] CallbackIo においては、送信スレッドと、受信 Callback が動作す
	// るスレッドの両方から同時に DispData() が Call される。
	// 仮に DispData() において、ロック制御（本 Sample では、EnterCritical
	// Section()、LeaveCriticalSection() にて行っている）を実施しない場合、
	// _TDSMssgFind() で (mode&0x8000)!=0、_TDSMssgNextL() で (mode&0x0080)
	// !=0 として、メッセージ名称、メッセージ項目名称を、メッセージ定義ファ
	// イルを参照して表示するように指定した場合、送受信の各スレッドから同時
	// に DispData() を Call することにより、_TDSMssgFind()、_TDSMssgNextL()
	// において競合が発生し、予期せぬ結果となる可能性がある。この場合 mode
	// の設定を変更し、メッセージ定義ファイルを参照しない設定とすること。
	// 下方の _TDSMssgNextL() の mode 設定値である dp に関して TDS.pdf の
	// 3.3 (2)(c) の注記を参照すること。

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

  if((MSSG_USE_FILE&0x80)!=0)	fm =0x8000;		// メッセージ定義を使用
  else				fm =0x0000;		// 使用せず
  	// fm の設定値 (_TDSMssgFind() における mode 値の設定値) に関して、上述
	// の [注意] を参照すること。

  if(msg!=0){
    if(tp==1){	dp|=0x3000;	fm|=0x3000;}		// 受信の場合
    else{	dp|=0x2000;	fm|=0x2000;}		// 送信の場合

    if((md=_TDSMssgFind(fm,msg,len,Fd,hd,str))	> 0){
      if(str[0]!='\0')	PO,"[%s]\n",str);		// メッセージ名称

      for(la=0;;){
	if(MSSG_USE_NEXTL==0){				// 項目値を取得
	  rtn=_TDSMssgNext (md,0,msg,&form,&sz,&noi,item,sizeof(item));
	  if(rtn					< 0)	break;
	  printf("%03o:%d*%2d:"		,form,sz,noi);
	  switch(form){					// 項目値の表示
							// 数値の２番目以降は省略
	    case 000: PO,"L[%d]\n"	,noi);			break;
	    case 010: PO,"B[%d]=%d\n"	,noi,itm->uc[0]);	break;
	    case 011: PO,"T[%d]=%d\n"	,noi,itm->uc[0]);	break;
	    case 020: PO,"A[%d]=%s\n"	,noi,itm->sc);		break;
	    case 021: PO,"J[%d]=%s\n"	,noi,itm->sc);		break;
	    case 022: PO,"K[%d]=%s\n"	,noi,itm->sc);		break;
	    case 030: PO,"I8[%d]=%lld\n",noi,itm->sl[0]);	break;
	    case 031: PO,"I1[%d]=%d\n"	,noi,itm->sc[0]);	break;
	    case 032: PO,"I2[%d]=%d\n"	,noi,itm->ss[0]);	break;
	    case 034: PO,"I4[%d]=%d\n"	,noi,itm->si[0]);	break;
	    case 040: PO,"F8[%d]=%le\n"	,noi,itm->fl[0]);	break;
	    case 044: PO,"F4[%d]=%e\n"	,noi,itm->fs[0]);	break;
	    case 050: PO,"U8[%d]=%llu\n",noi,itm->ul[0]);	break;
	    case 051: PO,"U1[%d]=%u\n"	,noi,itm->uc[0]);	break;
	    case 052: PO,"U2[%d]=%u\n"	,noi,itm->us[0]);	break;
	    case 054: PO,"U4[%d]=%u\n"	,noi,itm->ui[0]);	break;
	    default:  PO,"\n");					break;

	  }

	}else{						// リスト表示形式で取得
	  rtn=_TDSMssgNextL(md,dp,msg,&form,&noi,str,sizeof(str));
	  if(rtn					< 0)	break;
	  if((dp&0x70)>0x10){				// SML、NSG/TS300 の場合
	    for(i=la;i>rtn;i--) PO,"%*s>\n",i*2,"");	// '>' 出力
	  }
	  la=rtn;					// 現在の階層を保存
	  PO,"  %s\n",str);				// 取得した項目値を表示
	  if((dp&0x70)>0x10 && form==000 && noi==0) PO,"%*s>\n",(la+1)*2,"");
							// L0 の場合の '>' 処理
      } }
      if(MSSG_USE_NEXTL!=0){
        if((dp&0x70)>0x10){
	   for(i=la;i>0;i--) PO,"%*s>\n",i*2,"");	// 残りの '>' 表示
      } }
      _TDSMssgExit(md,0,msg);
  } }
}



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

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

  EnterCriticalSection(&Cs1);	// このロックは重要！ 詳細は DispSECSMssg() 参照
  if(rtn<0){					ok=1;
    if((rtn<(-E_NOTCONNECT) || (-E_ILLBLOCK)<rtn) && rtn!=(-E_NODATA)){
      _TDSErrorStatus(Fd,&err,&pos);
      PO,"ERROR  [%d.%d.%d]",rtn,err,pos);
      if      (rtn==(-ENODEV)){
	PO," : No such device ID\n");	len=ok=0;
      }else if(rtn==(-E2BIG )){
	PO," : Data size to large\n");	len=ok=0;
      }else			PO,"\n");
    }else{
      PO,"STATUS = %d : ",-rtn);
      switch(-rtn){
	case E_NODATA:		PO,"No data\n");		break;
	case E_ILLBLOCK:	PO,"Illegal block#\n");		break;
	case E_T1TIMEDOUT:	PO,"T1 Timeout occur\n");	break;
	case E_T2TIMEDOUT:	PO,"T2 Timeout occur\n");	break;
	case E_T3TIMEDOUT:	PO,"T3 Timeout occur\n");	break;
	case E_T4TIMEDOUT:	PO,"T4 Timeout occur\n");	break;
	case E_T5TIMEDOUT:	PO,"T5 Timeout occur\n");	break;
	case E_T6TIMEDOUT:	PO,"T6 Timeout occur\n");	break;
	case E_T7TIMEDOUT:	PO,"T7 Timeout occur\n");	break;
	case E_T8TIMEDOUT:	PO,"T8 Timeout occur\n");	break;
	case E_RETRYOVER:	PO,"Retry over\n");		break;
	case E_CONNECT:		PO,"Connected\n");		break;
	case E_SELECT:		PO,"Selected   (0x%04x)\n"
					,hd->did);		break;
	case E_REJECT:		PO,"Rejected XId=0x%04x.0x%04x\n"
					,hd->sid,hd->xid);	break;
	case E_DESELECT:	PO,"Deselected (0x%04x)\n"
					,hd->did);		break;
	case E_NOTCONNECT:	PO,"Not connected\n");		break;
	default:		PO,"\n");			break;
  } } }

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

  return(0);
}



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

static int
SendS1F1()
{
  TDSECSHead	hd;
  char		msg[16];
  int		rtn,sf=0x8101,len=0;

  		// (注) １次メッセージのトレース出力を _TDSCommSend() の後に実行
		// する場合、送信１次メッセージに対する相手からの２次メッセージ
		// の取得を（例えば Callback を使用して）別スレッドで行うと、そ
		// の２次メッセージのトレース出力の方が、ここでの１次メッセージ
		// のトレース出力よりも先に出力されるケースが発生する可能性があ
		// る。この現象を回避するためには、１次メッセージのトレース出力
		// を、_TDSCommSend() の前に行えばいいのだが、その場合、送信時の
		// TransactionID がその時点ではわからない (TransactionID は、
		// _TDSCommSend() の Call により、hd に戻って来る) ので、１次
		// メッセ−ジの TransactionID の表示はできないことになる。
		// 以上を考慮して、ユーザ側でのトレース出力位置、内容を決定する
		// 必要がある。
  if(FUNC_TYPE==0)	rtn=_TDSCommSend(Fd,0x0000,0,sf,  0,msg,len,&hd);
  else			rtn=_TDSUDrvSend(Fd,0x0000,0,sf,  0,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


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

static int
SendS1F2H(
unsigned int	did,
unsigned int	xid)
{
  TDSECSHead	hd;
  char		msg[256];
  int		rtn,md,sf=0x0102,len;
  
  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md =_TDSMssgInit   (   0,msg,sizeof(msg),Fd);		// S1F2
	_TDSMssgBuild  (md,0,msg,000,  0,  0);			// L0
    len=_TDSMssgEnd    (md,0,msg);

  }else{					// メッセージ定義を使用する
    //EnterCriticalSection(&Cs0);
	_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S1F2_H");
    len=_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


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

static int
SendS1F2E(
int		did,
unsigned int	xid)
{
  TDSECSHead	hd;
  char		msg[256];
  int		rtn,md,sf=0x0102,len;
  
  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md= _TDSMssgInit   (   0,msg,sizeof(msg),Fd);		// S1F2
	_TDSMssgBuild  (md,0,msg,000,  2,  0);			// L2
	_TDSMssgBuild  (md,0,msg,020,  6,"EQUIP1");		//  MDLN
	_TDSMssgBuild  (md,0,msg,020,  6,"01.000");		//  SOFTREV
    len=_TDSMssgEnd    (md,0,msg);

  }else{					// メッセージ定義を使用する
    //EnterCriticalSection(&Cs0);
	_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S1F2_E");
    len=_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


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

static int
SendS2F49()
{
  static int	cnt=0;
  TDSECSHead	hd;
  char		msg[1024];
  char		vb[4],str[128],itm[32];
  int		rtn,md,sf=0x8200+49,no1,no2,len,i,j;

  sprintf(str,"LOTID (%4d)",++cnt);	no1=(cnt%2)+1;	no2=(cnt%10)+1;
  
  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		_TDSMssgInit   (   0,msg,sizeof(msg),Fd);	// S2F49
  		_TDSMssgBuild  (md,0,msg,000,  3, 0);		// L3
    vb[0]=0;	_TDSMssgBuild  (md,0,msg,010,  1,vb);		//  DATAIDB
  		_TDSMssgBuild  (md,0,msg,020,  0,"LOAD");	//  RCMD
  		_TDSMssgBuild  (md,0,msg,000,  4, 0);		//  L4
    vb[0]=1;	_TDSMssgBuild  (md,0,msg,010,  1,vb);		//   STID
    vb[0]=0;	_TDSMssgBuild  (md,0,msg,010,  1,vb);		//   MTKD
  		_TDSMssgBuild  (md,0,msg,020, 20,str);		//   LOTID
    		_TDSMssgBuild  (md,0,msg,000,no1, 0);		//   L[no1]
    for(i=0;i<no1;i++){
    		_TDSMssgBuild  (md,0,msg,000,no2, 0);		//    L[no2]
      for(j=0;j<no2;j++){
    		_TDSMssgBuild  (md,0,msg,000,  2, 0);		//     L[2]
	sprintf(str,"WAFER(%04d-%d-%02d)",cnt,i+1,j+1);
  		_TDSMssgBuild  (md,0,msg,020, 20,str);		//      WAFERID
	sprintf(str,"PPID (%04d-%d-%02d)",cnt,i+1,j+1);
  		_TDSMssgBuild  (md,0,msg,020, 16,str);		//      PPID
    } }
    len=	_TDSMssgEnd    (md,0,msg);

  }else{					// メッセージ定義を使用する
    //EnterCriticalSection(&Cs0);
		_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S2F49");
  		_TDSMDMssgBuild(Md,0,msg,"LOTID",0,str);	//   LOTID
  		_TDSMDMssgBuild(Md,0,msg,"NOI1"	,1,&no1);	//   L[no1]
    for(i=0;i<no1;i++){
      sprintf(itm,"NOI2:%d"			,i+1);
  		_TDSMDMssgBuild(Md,0,msg,itm	,1,&no2);	//    L[no2]
      for(j=0;j<no2;j++){
	sprintf(itm,"WAFERID:%d:%d"		,i+1,j+1);
	sprintf(str,"WAFER[%04d-%d-%02d]",cnt	,i+1,j+1);
  		_TDSMDMssgBuild(Md,0,msg,itm	,0,str);	//      WAFERID
	sprintf(itm,"PPID:%d:%d"		,i+1,j+1);
	sprintf(str,"PPID [%04d-%d-%02d]",cnt	,i+1,j+1);
  		_TDSMDMssgBuild(Md,0,msg,itm	,0,str);	//      PPID
    } }
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;	// SendS1F1() の (注) を参照すること。
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,0,sf,  0,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,0,sf,  0,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


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

static int
SendS2F50(
int		did,
unsigned int	xid)
{
  static int	cnt=0;
  TDSECSHead	hd;
  char		msg[256];
  char		vb[4],str[128];
  int		rtn,md,sf=0x0200+50,len;
  
  sprintf(str,"LOTID (%4d)",++cnt);
  
  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		_TDSMssgInit   (   0,msg,sizeof(msg),Fd);	// S2F50
		_TDSMssgBuild  (md,0,msg,000,  2, 0);		// L2
    vb[0]=0;	_TDSMssgBuild  (md,0,msg,010,  1,vb);		//  HCACK
		_TDSMssgBuild  (md,0,msg,000,  2, 0);		//  L2
  		_TDSMssgBuild  (md,0,msg,020, 16,"PPID");	//   PPID
  		_TDSMssgBuild  (md,0,msg,020, 20,str);		//   LOTID
    len=	_TDSMssgEnd    (md,0,msg);

  }else{					// メッセージ定義を使用する
    //EnterCriticalSection(&Cs0);
		_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S2F50");
  		_TDSMDMssgBuild(Md,0,msg,"LOTID",  0,str);	//   LOTID
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


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

static int
SendS6F11()
{
  static int	cnt=0;
  TDSECSHead	hd;
  char		msg[256];
  unsigned short vs[4];
  int		rtn,md,sf=0x8600+11,len;

  if((++cnt)==65536) cnt=0;

  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		_TDSMssgInit   (   0,msg,sizeof(msg),Fd);	// S6F11
  		_TDSMssgBuild  (md,0,msg,000,  3, 0);		// L3
    vs[0]=cnt;	_TDSMssgBuild  (md,0,msg,052,  1,vs);		//  DATAID
    vs[0]=8;	_TDSMssgBuild  (md,0,msg,052,  1,vs);		//  CEID
  		_TDSMssgBuild  (md,0,msg,000,  3, 0);		//  L3
  		_TDSMssgBuild  (md,0,msg,020, 16,"DATA1 です");	//   DATA1
  		_TDSMssgBuild  (md,0,msg,020, 16,"DATA2 です");	//   DATA2
  		_TDSMssgBuild  (md,0,msg,020, 14,"YYYYMMDDhhmmss");
						// @TIME (本当は現在時刻を設定
						// するのだが、省略。
    len=	_TDSMssgEnd    (md,0,msg);

  }else{					// メッセージ定義を使用する
    //EnterCriticalSection(&Cs0);
		_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S6F11");
    vs[0]=cnt;	_TDSMDMssgBuild(Md,0,msg,"DATAID",1,vs);	//  DATAID
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;	// SendS1F1() の (注) を参照すること。
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,0,sf,  0,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,0,sf,  0,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



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

static int
SendS6F12(
int		did,
unsigned int	xid)
{
  TDSECSHead	hd;
  char		msg[256];
  char		vb[4];
  int		rtn,md,sf=0x0600+12,len;

  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		_TDSMssgInit (   0,msg,sizeof(msg),Fd);		// S6F12
    vb[0]=0;	_TDSMssgBuild(md,0,msg,010,  1,vb);		// ACKC
    len=	_TDSMssgEnd  (md,0,msg);

  }else{					// メッセージ定義を使用する
    //EnterCriticalSection(&Cs0);
		_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S6F12");
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



/* ---------------------------------------------------------------------------*/
/* 指定名称のメッセージをメッセージ定義ファイルを使用して構築し送信する ----- */

static int
SendNamedMssg(
int		did,
unsigned int	xid,
char		*mname)
{
  TDSECSHead	hd;
  char		msg[4096];			// 最大 4096Bytes までとする
  int		rtn=(-1),sf,scode,fcode,len;

						// メッセージ定義を使用しない
  if((MSSG_USE_FILE&0x80)				==0)	goto Exit;

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

  sscanf(mname,"S%dF%d"	,&scode,&fcode);
  sf = scode*0x0100 + fcode;
  if(scode!=9 && (fcode%2)!=0)	sf|=0x8000;

  //EnterCriticalSection(&Cs0);
  if(	_TDSMDMssgInit(Md,0,msg,sizeof(msg),mname)	< 0)	goto Exit;
  len=	_TDSMDMssgEnd (Md,0,msg);
  //LeaveCriticalSection(&Cs0);

  if(len<0)		rtn=len;	// SendS1F1() の (注) を参照すること。
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

Exit:
  return(rtn);
}



/* ========================================================================== */
/* Callback 関数 ============================================================ */

/* -------------------------------------------------------------------------- */
/* 相手からの SECS メッセージ受信用 Callback -------------------------------- */

static int
CBRecv(
void		*par,
TDSCBData	*cbd)
{
  TDSECSHead	hd;
  char		msg[1024],rname[32],sname[32];
  int		rtn,len,sf;

  DispData(1,&cbd->thd,cbd->msg,cbd->rtn,cbd->rtn);

  if      (cbd->req== 0 && cbd->rtn>=0){
    if((MSSG_USE_FILE&0x80)==0){	// メッセージ定義ファイルを使用しない
					// 場合は、自力で SF-Code を調べ、２次
					// メッセージの送出の必要性を判断し、
					// 必要な場合は、送出する。
      if     (cbd->sf==(0x8100+ 1)&&OType==0)	SendS1F2H (cbd->devid,cbd->xid);
      else if(cbd->sf==(0x8100+ 1)&&OType==1)	SendS1F2E (cbd->devid,cbd->xid);
      else if(cbd->sf==(0x8200+49))		SendS2F50 (cbd->devid,cbd->xid);
      else if(cbd->sf==(0x8600+11))		SendS6F12 (cbd->devid,cbd->xid);

    }else if((cbd->sf&0x8000)!=0){	// メッセージ定義ファイルを使用する
					// 場合は、自動返信機能を使用する。
					//（使用しなくてはいけない訳ではない。）
      memcpy(msg,cbd->msg,cbd->rtn);
      //EnterCriticalSection(&Cs0);
      len=_TDSMDMssgAutoRes(Md,0,&cbd->thd
		,msg,cbd->rtn,sizeof(msg),rname,sname,&sf);
      //LeaveCriticalSection(&Cs0);
      if(len							>=0){
	PO,"RECV %s ..  Auto respond %s [S%dF%d]\n"
		,rname,sname,sf/0x100,sf&0xff);
	if(FUNC_TYPE==0){
	  rtn=_TDSCommSend (Fd,0x0000,cbd->devid,sf,cbd->xid,msg,len,&hd);
	}else{
	  rtn=_TDSUDrvSend (Fd,0x0000,cbd->devid,sf,cbd->xid,msg,len,&hd);
	}
	DispData(2,&hd,msg,len,rtn);
      }else{
	if(len!=(-930) && len!=(-931)){
	  PE,"RECV Auto response error (%d)\n",len);
  } } } }

  return(0);
}



/* -------------------------------------------------------------------------- */
/* 送信結果受信用 Callback -------------------------------------------------- */

static int
CBSend(
void		*par,
TDSCBData	*cbd)
{
  DispData(0,&cbd->thd,cbd->msg,cbd->rtn,cbd->rtn);
  return(0);
}



/* -------------------------------------------------------------------------- */
/* ユーザ・トレース出力関数 ------------------------------------------------- */

static int
CBTrace(
void		*par,
TDSTRData	*trd)
{
  PO,"UserTrace [%08d.%06d.%03d] (%4d) : %s\n"
	,trd->date,trd->time,trd->msec,trd->proc,trd->msg);
  return(0);
}



/* ========================================================================== */
/* Signal handler ----------------------------------------------------------- */

static void
CBSignal(
int		vec)
{
  signal(SIGINT,CBSignal);
  Break=1;
}
